线段树维护等差数列

线段树维护等差数列

先整个简单的Luogu1438 无聊的数列

这个确实简单,直接前缀和就行了......

真正的等差数列

上面那个就是个\(ZZ\)题,所以我们如何维护等差数列可以做到区间求和呢??

你发现等差数列是这样的:\(a,a+b,a+2b,...,a+nb\)

那要是这样的话,我们发现\(b\)的倍数是\(1,2,3,...\)

所以我们这样设计线段树\(1a_1,2a_2,3a_3,...\)

\(a\)就是我们线段树每一个节点的值,那么我们直接对线段树进行区间加,也就是对等差数列的区间加

于是你发现我们加的一定是公差,但是可能首项就不一样了,那么我们就再加一个\(b\)

维护多出来或者少的那一部分值,把首项变成正确的

于是一个带系数的线段树,和一个普通线段树,共同构成了等差数列

这两个当然可以放在同一个线段树里啦!!

所以我做这个第一个题就是BZOJ 3221

标记永久化,我记得了,主席树的区间修改+区间查询(也可以单点查询修改,好像标记永久化很方便的亚子,主要针对可加性的变化)

code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
inline int read(){
    int s=0,t=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')t=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return s*t;
}
const int N=1e6+5;
int n,q,lans;
int to[N*2],nxt[N*2],head[N],rp;
void add_edg(int x,int y){
    to[++rp]=y;
    nxt[rp]=head[x];
    head[x]=rp;
}
int son[N],siz[N],dep[N],fa[N];
int top[N],dfn[N],idf[N],cnt;
void dfs_fi(int x){
    son[x]=0;siz[x]=1;
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==fa[x])continue;
        fa[y]=x;dep[y]=dep[x]+1;
        dfs_fi(y);
        siz[x]+=siz[y];
        if(!son[x]||siz[y]>siz[son[x]])son[x]=y;
    }
}
void dfs_se(int x,int f){
    top[x]=f;dfn[x]=++cnt;idf[cnt]=x;
    if(son[x])dfs_se(son[x],f);
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==son[x]||y==fa[x])continue;
        dfs_se(y,y);
    }
}
int rz[N],rf[N],cur,tot;
const int de=18;
struct ZXS{
    int sum[N*de],tag[N*de],taf[N*de];
    int tp[N*de],ls[N*de],rs[N*de];
    int seg;
    int newpot(int x){
        ++seg;sum[seg]=sum[x];tag[seg]=tag[x];
        taf[seg]=taf[x];tp[seg]=cur;
        ls[seg]=ls[x];rs[seg]=rs[x];
        return seg;
    }
    void ins(int &x,int l,int r,int ql,int qr,int v,int w){
        if(ql>qr)return ;
        x=newpot(x);
        if(ql<=l&&r<=qr){
            tag[x]+=v;taf[x]+=w;
            return ;
        }
        int s=max(l,ql),t=min(r,qr);
        sum[x]+=v*(s+t)*(t-s+1)/2+w*(t-s+1);
        int mid=l+r>>1;
        if(ql<=mid)ins(ls[x],l,mid,ql,qr,v,w);
        if(qr>mid)ins(rs[x],mid+1,r,ql,qr,v,w);
        return ;
    }
    int query(int x,int l,int r,int ql,int qr){
        if(!x||ql>qr)return 0;
        int s=max(l,ql),t=min(r,qr);
        int ret=tag[x]*(s+t)*(t-s+1)/2+taf[x]*(t-s+1);
        if(ql<=l&&r<=qr)return ret+sum[x];
        int mid=l+r>>1;
        if(ql<=mid)ret+=query(ls[x],l,mid,ql,qr);
        if(qr>mid)ret+=query(rs[x],mid+1,r,ql,qr);
        return ret;
    }
}zs,fs;
int LCA(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        x=fa[top[x]];
    }
    return dep[x]<dep[y]?x:y;
}
void change(int x,int y,int a,int b){
    int lca=LCA(x,y),dis=dep[x]+dep[y]-2*dep[lca]+1;
    int sx=a;
    while(top[x]!=top[lca]){
        fs.ins(rf[cur],1,n,n-dfn[x]+1,n-dfn[top[x]]+1,b,sx-b*(n-dfn[x]+1));
        sx+=b*(dfn[x]-dfn[top[x]]+1);
        x=fa[top[x]];
    }
    fs.ins(rf[cur],1,n,n-dfn[x]+1,n-dfn[lca]+1,b,sx-b*(n-dfn[x]+1));
    sx=a+b*(dis-1);
    while(top[y]!=top[lca]){
        zs.ins(rz[cur],1,n,dfn[top[y]],dfn[y],b,sx-b*(dfn[y]-dfn[top[y]])-b*dfn[top[y]]);
        sx-=b*(dfn[y]-dfn[top[y]]+1);
        y=fa[top[y]];
    }
    zs.ins(rz[cur],1,n,dfn[lca]+1,dfn[y],b,sx-b*(dfn[y]-dfn[lca]-1)-b*(dfn[lca]+1));
    return ;
}
int query(int x,int y){
    int lca=LCA(x,y),ret=0;
    while(top[x]!=top[lca]){
        ret+=fs.query(rf[cur],1,n,n-dfn[x]+1,n-dfn[top[x]]+1);
        ret+=zs.query(rz[cur],1,n,dfn[top[x]],dfn[x]);
        x=fa[top[x]];
    }
    ret+=fs.query(rf[cur],1,n,n-dfn[x]+1,n-dfn[lca]+1);
    ret+=zs.query(rz[cur],1,n,dfn[lca],dfn[x]);
    while(top[y]!=top[lca]){
        ret+=fs.query(rf[cur],1,n,n-dfn[y]+1,n-dfn[top[y]]+1);
        ret+=zs.query(rz[cur],1,n,dfn[top[y]],dfn[y]);
        y=fa[top[y]];
    }
    ret+=fs.query(rf[cur],1,n,n-dfn[y]+1,n-dfn[lca]);
    ret+=zs.query(rz[cur],1,n,dfn[lca]+1,dfn[y]);
    return ret;
}
signed main(){
    n=read();q=read();
    fo(i,2,n){
        int x=read(),y=read();
        add_edg(x,y);
        add_edg(y,x);
    }
    dfs_fi(1);dfs_se(1,1);
    while(q--){
        char t[10];int x,y,a,b;
        scanf("%s",t+1);
        if(t[1]=='c'){
            tot++;rz[tot]=zs.newpot(rz[cur]);
            rf[tot]=fs.newpot(rf[cur]);cur=tot;
            x=read()^lans,y=read()^lans,a=read(),b=read();
            change(x,y,a,b);
        }
        if(t[1]=='q'){
            x=read()^lans,y=read()^lans;
            printf("%lld\n",lans=query(x,y));
        }
        if(t[1]=='l'){
            cur=read()^lans;
        }
    }
}

然鹅一开始我写的时候就下放标记了,差点猝死

神奇的线段树,啥都能干!!!

posted @ 2021-12-12 21:34  fengwu2005  阅读(672)  评论(0编辑  收藏  举报