ural1855 线段树区间更新+推公式维护一元二次式

和威威猫系列故事差不多,都是根据条件推出公式

/*
操作c a b d:a到b道路上的所有边权值加d
操作e a b:问a到b中包含的道路的平均权值
区间平均值=所有可能路径权值/所有路径数,
而路径数=len*(len+1)/2,那么只要求区间中所有路径的权值即可
对于属于路径[L,R]的路径[k,k+1](设为x路径),那么可推出有(K-L+1)(R-K)条路径经过x路径,则路径x对于[L,R]的贡献就是w[k]*(K-L+1)(R-K)
化简得seg[k]*(-k*k+(R+L-1)*k+R*(1-L)),那么只要维护一元二次式中k的三个系数即可

那么[L,R]中所有路径权值的和就是sum{seg[k] * (-k*k + (R-L+1)*k + R*(1-L)}
                            =-sum{seg[k]*k*k} + (R-L+1)sum{seg[k]*k} + R*(1-L)sum{seg[k]}
一颗线段树add维护区间累加的值,三颗线段sum1,sum2,sum3维护三个独立的sum,一颗二维mul线段树维护区间k*k,k,1的值(固定的)
另外将道路离散化成点[L,R]中的每个k可以对应[L,R-1]
*/
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long 
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define maxn 100005
ll sum[maxn<<2],sumk[maxn<<2],sumkk[maxn<<2];
ll add[maxn<<2],multi[maxn<<2][3];
int N,M;
ll S,SK,SKK;//分别是三个系数的和
inline void pushup(int rt){
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
    sumk[rt]=sumk[rt<<1]+sumk[rt<<1|1];
    sumkk[rt]=sumkk[rt<<1]+sumkk[rt<<1|1];
}
//区间每次加上Add都要进行更新,Add对每个sum的贡献量就是Add乘以multi
inline void maintain(int rt,ll Add){
    sum[rt]+=multi[rt][0]*Add;
    sumk[rt]+=multi[rt][1]*Add;
    sumkk[rt]+=multi[rt][2]*Add;
}
inline void pushdown(int rt){
    if(add[rt]){
        add[rt<<1]+=add[rt];
        add[rt<<1|1]+=add[rt];
        maintain(rt<<1,add[rt]);
        maintain(rt<<1|1,add[rt]);
        add[rt]=0;
    }
}
void build(int l,int r,int rt){
    if(l==r){
        sum[rt]=sumk[rt]=sumkk[rt]=0;
        add[rt]=0;
        multi[rt][0]=1;
        multi[rt][1]=(ll)r;
        multi[rt][2]=(ll)r*r;
        return;
    }
    int m=l+r>>1;
    build(lson);
    build(rson);
    pushup(rt);
    for(int i=0;i<3;i++)
        multi[rt][i]=multi[rt<<1][i]+multi[rt<<1|1][i];

}
void update(int L,int R,ll C,int l,int r,int rt){
    if(L<=l && R>=r){
        add[rt]+=C;
        maintain(rt,C);
        return;
    }
    pushdown(rt);
    int m=l+r>>1;
    if(L<=m) update(L,R,C,lson);
    if(R>m) update(L,R,C,rson);
    pushup(rt);
}
//查询区间[L,R],求出区间[L,R]的三个sum分别是多少
void query(int L,int R,int l,int r,int rt){
    if(L<=l && R>=r){
        S+=sum[rt];
        SK+=sumk[rt];
        SKK+=sumkk[rt];
        return;
    }
    pushdown(rt);
    int m=l+r>>1;
    if(L<=m) query(L,R,lson);
    if(R>m) query(L,R,rson);
}

int main(){
    while(scanf("%d%d",&N,&M)==2){
        build(1,N,1);
        char s[20];
        for(int i=1;i<=M;i++){
            scanf("%s",s);
            if(s[0]=='c'){
                int a,b,c;
                scanf("%d%d%d",&a,&b,&c);
                update(a,b-1,(ll)c,1,N,1);
            }
            else {
                int a,b;
                scanf("%d%d",&a,&b);
                S=(ll)0;
                SK=(ll)0;
                SKK=(ll)0;
                query(a,b-1,1,N,1);
                double ans=-SKK+((ll)a+b-1)*SK+(ll)b*(1-a)*S;
                ll tot=(ll)(b-a)*(b-a+1)/2;
                printf("%.10f\n",ans/tot);
            }
        }
    }
    return 0;
}

 

posted on 2018-11-09 20:39  zsben  阅读(173)  评论(0编辑  收藏  举报

导航