题解 P4915 【帕秋莉的魔导书】
SuperJvRuo
2018-10-06 12:31:09
此题的弱化版:[P4868 Preprefix sum](https://www.luogu.org/problemnew/show/P4868)
~~暑假的时候出题人请我验了这个idea,然后我随口就口胡了正解。~~
简化一下题意:
1. 求$$\frac{\sum_{i=l}^r\sum_{j=1}^iw_i}{r-l+1}$$
2. 单点修改$w_i$。
操作1就是求$\frac{(\sum_{i=1}^r\sum_{j=1}^iw_i)-(\sum_{i=1}^{l-1}\sum_{j=1}^iw_i)}{r-l+1}$,也就是两次查询前缀的前缀和,这样就转化为了上面的原题。
而对于求原题中的$SS_i=\sum^i_{j=1}\sum^j_{k=1}a_k$,展开$SS_i$,发现$SS_i=\sum^i_{j=1}(i-j+1)\times a_j$。
原题可以维护两个树状数组,一个维护$a_i$,另一个维护$b_i=(n-i+1)\times a_i$。这样,我们要查询的$SS_i$即为$\sum^i_{j=1}[(n-j+1)-(n-i)]\times a_j=\sum^i_{j=1}b_j-(n-i)\times\sum^i_{j=1}a_j$。
当然,此题值域较大,需要用动态开点线段树代替树状数组。
```
#include<cstdio>
#include<cctype>
#include<climits>
#define LL long long
int Read()
{
int x=0;char c=getchar();
while(!isdigit(c))
{
c=getchar();
}
while(isdigit(c))
{
x=x*10+(c^48);
c=getchar();
}
return x;
}
struct Node
{
int ch[2];
LL val,presum;
}tree[2000005];
int root=1,cnt=1;
void Update(int idx)
{
tree[idx].val=tree[tree[idx].ch[0]].val+tree[tree[idx].ch[1]].val;
tree[idx].presum=tree[tree[idx].ch[0]].presum+tree[tree[idx].ch[1]].presum;
}
void Add(int pos,int val,int l=0,int r=INT_MAX,int idx=1)
{
if(l==r)
{
tree[idx].val+=val;
tree[idx].presum+=(LL)val*(INT_MAX-pos+1);
}
else
{
int mid=l+r>>1;
if(pos<=mid)
{
if(!tree[idx].ch[0])//这就是动态开点
{
tree[idx].ch[0]=++cnt;
}
Add(pos,val,l,mid,tree[idx].ch[0]);
}
else
{
if(!tree[idx].ch[1])
{
tree[idx].ch[1]=++cnt;
}
Add(pos,val,mid+1,r,tree[idx].ch[1]);
}
Update(idx);
}
}
LL Query(int pos,int l=0,int r=INT_MAX,int idx=1)
{
if(r<=pos)
{
return tree[idx].presum-tree[idx].val*(INT_MAX-pos);
}
else
{
int mid=l+r>>1;
LL res=Query(pos,l,mid,tree[idx].ch[0]);
if(mid<pos)
{
res+=Query(pos,mid+1,r,tree[idx].ch[1]);
}
return res;
}
}
int main()
{
int n=Read(),m=Read(),a,w;
while(n--)
{
a=Read(),w=Read();
Add(a,w);
}
while(m--)
{
if(Read()==1)
{
a=Read(),w=Read();
double res=((long double)Query(w)-Query(a-1))/(w-a+1);
printf("%.4lf\n",res);
}
else
{
a=Read(),w=Read();
Add(a,w);
}
}
return 0;
}
```