[HAOI2012] 高速公路
首先,根据题目的数据,我们将边权下放到点权,之后用线段树维护。
对于一个带查询区间 [l,r] ,我们可以依次考虑每个点对答案的贡献。
对于点 i ,经过它的起点有 ( i-l+1 ) 种选择,终点有 ( r-i+1 ) 种选择,根据乘法原理,共有 ( i-l+1 ) * ( r-i+1 ) 条路径经过它。
注意这里,因为我们将边权下放到点权,所以单点依然代表一条路径,不要漏掉。
那么这个区间的所有点的总贡献为 tot = Σ w[i] * ( i-l+1) * ( r-i+1 ),同时易求得共有 ( 1+r-l+1 ) * ( r-l+1 ) / 2 种可能路径,两者相除即为期望值。
现在我们考虑如何求 tot。
按照常规思路,我们提常数项。
tot = Σ w[i] * ( i-l+1) * ( r-i+1 )
= Σ w[i] * ( i*r - i*i + i - l*r + l*i - l + r - i + 1 )
= Σ w[i] * ( ( r-l*r-l+1 )+ ( r+l ) * i - i*i )
= ( r-l*r-l+1) * Σ w[i] + ( r+l ) * Σ w[i] * i - Σ w[i] * i*i
这样一来,我们可以考虑维护 Σ w[i],Σ w[i] * i,Σ w[i] * i*i 这三项来得到答案。
但是这三项好像无法直接维护。
我们再看题目,题目的操作是每次给区间一个增值 x,也就是说对于给定区间,所有 w[i] = x,这样一来我们只需知道 Σ1,Σi,Σi*i,便可以成功维护区间信息,得到解了。
而对于 Σi*i 如何求,数学大佬们都是上公式,像我这种数学渣渣,只会前缀和预处理。
不过前缀和可比公式快多咯。
所以我要再次提醒自己注意中间变量爆精度......!
// q.c // 中间变量爆精度这种事真的会死人的... #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; typedef long long LL; const int M=100000+10; int n,m; LL s[M][2]; struct Node { int l,r; LL s1,s2,s3,lazy; Node():l(0),r(0),s1(0),s2(0),s3(0),lazy(0) {} }; struct SegmentTree { int root; Node nd[M<<2]; SegmentTree():root(0) {} void update(int o) { Node &p=nd[o],lc=nd[o<<1],rc=nd[o<<1^1]; p.s1=lc.s1+rc.s1; p.s2=lc.s2+rc.s2; p.s3=lc.s3+rc.s3; } void pushdown(int o) { if(nd[o].lazy) { Node &p=nd[o],&lc=nd[o<<1],&rc=nd[o<<1^1]; lc.s1+=(lc.r-lc.l+1)*p.lazy; lc.s2+=(s[lc.r][0]-s[lc.l-1][0])*p.lazy; lc.s3+=(s[lc.r][1]-s[lc.l-1][1])*p.lazy; rc.s1+=(rc.r-rc.l+1)*p.lazy; rc.s2+=(s[rc.r][0]-s[rc.l-1][0])*p.lazy; rc.s3+=(s[rc.r][1]-s[rc.l-1][1])*p.lazy; lc.lazy+=p.lazy; rc.lazy+=p.lazy; p.lazy-=p.lazy; } } void build(int o,int l,int r) { nd[o].l=l,nd[o].r=r; if(l==r) return ; int mid=(l+r)>>1; build(o<<1,l,mid); build(o<<1^1,mid+1,r); } void add(int o,int l,int r,int x) { Node &p=nd[o]; if(l<=p.l&&p.r<=r) { p.s1+=(p.r-p.l+1)*(LL)x; p.s2+=(s[p.r][0]-s[p.l-1][0])*x; p.s3+=(s[p.r][1]-s[p.l-1][1])*x; p.lazy+=x; } else { pushdown(o); int mid=(p.l+p.r)>>1; if(l<=mid) add(o<<1,l,r,x); if(r>mid) add(o<<1^1,l,r,x); update(o); } } Node query(int o,int l,int r) { Node &p=nd[o]; if(l<=p.l&&p.r<=r) return p; else { pushdown(o); int mid=(p.l+p.r)>>1; Node lc,rc; if(l<=mid) lc=query(o<<1,l,r); if(r>mid) rc=query(o<<1^1,l,r); lc.s1+=rc.s1; lc.s2+=rc.s2; lc.s3+=rc.s3; return lc; } } }t; LL gcd(LL a,LL b) { return !b?a:gcd(b,a%b); } void solve(int l,int r) { Node p=t.query(t.root,l,r); LL x=(r-(LL)l*r-l+1)*p.s1+(r+l)*p.s2-p.s3; // 爆. LL y=(LL)(r-l+2)*(r-l+1)/2; // 爆. LL d=gcd(x,y); x/=d,y/=d; printf("%lld/%lld\n",x,y); } int main() { freopen("roadxw.in","r",stdin); freopen("roadxw.out","w",stdout); scanf("%d%d",&n,&m); --n; for(int i=1;i<=n;i++) { s[i][0]=s[i-1][0]+(LL)i; s[i][1]=s[i-1][1]+(LL)i*i; } t.build(1,1,n); char opt[3]; int l,r,x; for(int i=1;i<=m;i++) { scanf("%s%d%d",opt,&l,&r); if(opt[0]=='C') scanf("%d",&x),t.add(t.root,l,r-1,x); else solve(l,r-1); } return 0; }