P2221 [HAOI2012]高速公路(线段树)

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;
}

猜你喜欢

转载自www.cnblogs.com/kafuuchino/p/10610089.html