P2221 [HAOI2012]高速公路(线段树)
显然答案为 $\dfrac{\sum_{i=l}^r\sum_{j=l}^{r}dis[i][j]}{C_{r-l+1}^2}$
下面倒是挺好算,组合数瞎搞
上面咋算呢
先考虑每条边被算上的次数$ans = \sum_{i=l}^{r}a[i]*(r-i+1)(i-l+1)$
我们把它拆开再合并瞎搞,按变量$i$的次数分项
蓝后化出来这个式子:
$ans = (r - l- r*l+1) *S_{1}+ (l+r)*S_{2}-S_{3}$
$S_{1} = \sum_{i=l}^{r} a[i]$
$S_{2} = \sum_{i=l}^{r} a[i]*i$
$S_{3} = \sum_{i=l}^{r} a[i]*i*i$
显然这是可以用线段树维护的辣
区间添加$k$时
显然$S_{1}+=(r-l+1)*k$
$S_{2}+=\sum i *k$
$S_{3}+=\sum i*i *k$
再开俩数组维护下$S_{4}=\sum i $和$S_{5}=\sum i*i$就好辣
注意我们是按边开线段树,所以$r-=1$,组合数也要改为$C_{r-l+1}^2$
#include<iostream> #include<cstdio> #include<cstring> using namespace std; typedef long long ll; void read(int &x){ char c=getchar();x=0; int f=1; while(c<'0'||c>'9') f=f&&(c!='-'),c=getchar(); while('0'<=c&&c<='9') x=x*10+(c^48),c=getchar(); x=f?x:-x; } #define W 400005 int n,m; ll S1,S2,S3,ans,tot,g; ll add[W],s1[W],s2[W],s3[W],s4[W],s5[W]; #define lc o<<1 #define rc o<<1|1 #define mid (l+r)/2 inline void up(int o){ s1[o]=s1[lc]+s1[rc], s2[o]=s2[lc]+s2[rc], s3[o]=s3[lc]+s3[rc]; } void down(int o,int l,int r){ if(!add[o]) return ; s1[lc]+=1ll*(mid-l+1)*add[o], s1[rc]+=1ll*(r-mid)*add[o]; s2[lc]+=s4[lc]*add[o], s2[rc]+=s4[rc]*add[o]; s3[lc]+=s5[lc]*add[o], s3[rc]+=s5[rc]*add[o]; add[lc]+=add[o], add[rc]+=add[o]; add[o]=0; } void build(int o,int l,int r){ if(l==r){s4[o]=l,s5[o]=1ll*l*l; return ;} build(lc,l,mid); build(rc,mid+1,r); s4[o]=s4[lc]+s4[rc], s5[o]=s5[lc]+s5[rc]; } void Add(int o,int l,int r,int x1,int x2,int v){ if(x1<=l&&r<=x2){ add[o]+=v, s1[o]+=(r-l+1)*v, s2[o]+=s4[o]*v, s3[o]+=s5[o]*v; return ; }down(o,l,r); if(x1<=mid) Add(lc,l,mid,x1,x2,v); if(x2>mid) Add(rc,mid+1,r,x1,x2,v); up(o); } void Ask(int o,int l,int r,int x1,int x2){ if(x1<=l&&r<=x2){ S1+=s1[o], S2+=s2[o], S3+=s3[o]; return ; }down(o,l,r); if(x1<=mid) Ask(lc,l,mid,x1,x2); if(x2>mid) Ask(rc,mid+1,r,x1,x2); } ll gcd(ll a,ll b){return b?gcd(b,a%b):a;} int main(){ char opt[3]; int l,r,v; read(n);read(m); --n; build(1,1,n); while(m--){ scanf("%s",opt); read(l);read(r); --r; if(opt[0]=='C') read(v),Add(1,1,n,l,r,v); else{ S1=S2=S3=0; Ask(1,1,n,l,r); ans=1ll*(r-l-1ll*l*r+1)*S1+1ll*(l+r)*S2-S3; tot=1ll*(r-l+2)*(r-l+1)/2; g=gcd(ans,tot); ans/=g; tot/=g; printf("%lld/%lld\n",ans,tot); } }return 0; }