[Luogu 2221] HAOI2012 高速公路
比较容易看出的线段树题目。
由于等概率,期望便转化为 子集元素和/子集个数。
每一段l..r中,子集元素和为:
(sum w_{i}(i-l+1)(r-i)) //((i-l+1)(r-i))是每个数用到的次数
(=sum w_{i}((r-lr)+(l+r-1)i-i^{2}))
(=(r-lr)sum w_{i}+(l+r-1)sum i imes w_{i}-sum i^{2} imes w_{i})
由此观之,线段树需要维护(sum w_{i})v[0]
、(sum i imes w_{i})v[1]
、(sum i^{2} imes w_{i})v[2]
。
有这样一个神奇的公式:
(1^{2}+2^{2}+dots+n^{2}=n(n+1)(2n+1)/6)
所以,在进行Update操作时,设size=r-l+1
,改变量为v
:
v[0]+=v*size;
v[1]+=v*size*(l+r)>>1;
v[2]+=v*(r*(r+1)*((r<<1)+1)-(l-1)*l*((l<<1)-1))/6LL;
记得开long long以及各种强制转int为long long!隐式类型转换简直天坑。
线段树基础不扎实的我这题调了一天…
#include <cstdio>
#include <cstring>
const int MAXN=100010;
int n,m;
class SegmentTree
{
public:
SegmentTree(void)
{
memset(s,0,sizeof s);
}
void BuildTree(int i,int l,int r)
{
s[i].l=l,s[i].r=r;
if(l==r)
return;
int j=i<<1,mid=l+r>>1;
BuildTree(j,l,mid),BuildTree(j+1,mid+1,r);
}
void Add(int i,int l,int r,long long v)
{
if(l==s[i].l && r==s[i].r)
{
Update(i,v);
return;
}
if(s[i].l!=s[i].r && s[i].lazy)
PushDown(i);
int j=i<<1,mid=s[i].l+s[i].r>>1;
if(r<=mid)
Add(j,l,r,v);
else if(l>mid)
Add(j+1,l,r,v);
else
Add(j,l,mid,v),Add(j+1,mid+1,r,v);
PushUp(i);
}
void Ans(long long l,long long r)
{
long long t,ans,cnt=(r-l+1)*(r-l)>>1LL,sum[3];
for(int i=0;i<3;++i)
sum[i]=Sum(1,l,r-1,i);
ans=sum[0]*(r-l*r)+sum[1]*(l+r-1)-sum[2];
t=GCD(ans,cnt);
printf("%lld/%lld
",ans/t,cnt/t);
}
private:
struct node
{
int l,r;
long long lazy,v[3];
}s[MAXN<<2];
long long GCD(long long x,long long y)
{
return !y ? x : GCD(y,x%y);
}
void Update(int i,long long v)
{
long long l=s[i].l,r=s[i].r,size=r-l+1;
s[i].lazy+=v;
s[i].v[0]+=v*size;
s[i].v[1]+=v*size*(l+r)>>1;
s[i].v[2]+=v*(r*(r+1)*((r<<1)+1)-(l-1)*l*((l<<1)-1))/6LL;
}
void PushUp(int i)
{
for(int j=0;j<3;++j)
s[i].v[j]=s[i<<1].v[j]+s[i<<1|1].v[j];
}
void PushDown(int i)
{
int j=i<<1;
Update(j,s[i].lazy),Update(j+1,s[i].lazy);
s[i].lazy=0;
}
long long Sum(int i,int l,int r,int k)
{
if(l==s[i].l && r==s[i].r)
return s[i].v[k];
if(s[i].l!=s[i].r && s[i].lazy)
PushDown(i);
int j=i<<1,mid=s[i].l+s[i].r>>1;
if(r<=mid)
return Sum(j,l,r,k);
else if(l>mid)
return Sum(j+1,l,r,k);
else
return Sum(j,l,mid,k)+Sum(j+1,mid+1,r,k);
}
}T;
int main(int argc,char *argv[])
{
scanf("%d %d",&n,&m);
T.BuildTree(1,1,n-1);
for(int i=1,l,r,v;i<=m;++i)
{
char c;
scanf("
%c %d %d",&c,&l,&r);
if(c=='C')
{
scanf("%d",&v);
T.Add(1,l,r-1,v);
}
else
T.Ans(l,r);
}
return 0;
}
谢谢阅读。