BZOJ 3221 [Codechef FEB13] Obserbing the tree树上询问 树链剖分 主席树维护区间加等差数列

本文介绍了一种利用树链剖分结合主席树的数据结构解决方案,用于处理树形结构上的路径操作,包括路径上节点值的批量修改及查询历史版本节点值之和等问题。该方法能够有效地支持区间修改和查询操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:给出一棵树,每个结点初始均为0,要求支持以下操作:路径加等差数列,询问路径上结点权值和,回到之前的某个版本,强制在线。

调到吐血。

很明显可以看出需要树链剖分+主席树来解决,由于需要在主席树上区间修改所以标记永久化。
区间维护等差数列需要在每个结点上维护 首项 和 公差。
树链剖分时在路径上加等差数列需要讨论,细节比较多,详见代码。
(我看错题了..注意回到某个版本并不舍弃在这之间的版本)

#include <cstdio>
#include <algorithm>
#include <cstring>
#define N 150005
#define int long long
using namespace std;
typedef long long LL;
int time_clock,tot_time;
namespace Segment_Tree {
    struct Node {
        Node* ch[2];
        int sum,a0,d,l,r,ver;
        Node(Node* tmp=NULL) {
            if(tmp) {
                ch[0]=tmp->ch[0];
                ch[1]=tmp->ch[1];
                sum=tmp->sum;
                a0=tmp->a0;
                d=tmp->d;
                l=tmp->l;
                r=tmp->r;
                ver=tmp->ver;
            }
            else {
                ch[0]=ch[1]=NULL;
                sum=l=r=a0=d=ver=0;
            }
        }
        void* operator new(size_t) {
            static Node *C,*mempool;
            if(C==mempool) mempool=(C=new Node[1<<20])+(1<<20);
            return C++;
        }
    }*root[N];
    void init(Node*& o,int l,int r) {
        o=new Node();
        o->l=l, o->r=r;
        if(l==r) return ;
        int mid=l+r>>1;
        init(o->ch[0],l,mid), init(o->ch[1],mid+1,r);
        return ;
    }
    int query(Node* o,int l,int r) {
        int tmp=(2*o->a0+o->d*(l+r-2*o->l))*(r-l+1)/2;
        if(o->l==l && o->r==r) return o->sum+tmp;
        int mid=o->l+o->r>>1;
        if(r<=mid) return tmp+query(o->ch[0],l,r);
        if(l>mid) return tmp+query(o->ch[1],l,r);
        return tmp+query(o->ch[0],l,mid)+query(o->ch[1],mid+1,r);
    }
    void change(Node*& o,int l,int r,int a0,int d) {
        if(o->ver!=time_clock) {
            Node* tmp=o;
            o=new Node(tmp);
            o->ver=time_clock;
        }
        if(o->l==l && o->r==r) {
            o->a0+=a0;
            o->d+=d;
            return ;
        }
        o->sum+=(2*a0+d*(r-l))*(r-l+1)/2;
        int mid=o->l+o->r>>1;
        if(r<=mid) change(o->ch[0],l,r,a0,d);
        else if(l>mid) change(o->ch[1],l,r,a0,d);
        else change(o->ch[0],l,mid,a0,d), change(o->ch[1],mid+1,r,a0+d*(mid+1-l),d);
        return ;
    }
}
namespace Tree_Partition {
    struct Edge {
        int from,to,nxt;
        Edge() {}
        Edge(int _from,int _to,int _nxt):from(_from),to(_to),nxt(_nxt) {}
    }e[N*2];
    int fir[N],tot=-1;
    void Add_Edge(int x,int y) {
        e[++tot]=Edge(x,y,fir[x]), fir[x]=tot;
        e[++tot]=Edge(y,x,fir[y]), fir[y]=tot;
        return ;
    }
    int dfs_clock,pa[N],top[N],dpt[N],siz[N],son[N],pos[N],seq[N];
    void dfs1(int x) {
        siz[x]=1;
        dpt[x]=dpt[pa[x]]+1;
        for(int i=fir[x];~i;i=e[i].nxt) {
            if(e[i].to==pa[x]) continue;
            pa[e[i].to]=x;
            dfs1(e[i].to);
            siz[x]+=siz[e[i].to];
            if(siz[son[x]]<siz[e[i].to]) son[x]=e[i].to;
        }
        return ;
    }
    void dfs2(int x) {
        pos[x]=++dfs_clock;
        seq[dfs_clock]=x;
        if(son[pa[x]]==x) top[x]=top[pa[x]];
        else top[x]=x;
        if(son[x]) dfs2(son[x]);
        for(int i=fir[x];~i;i=e[i].nxt) {
            if(e[i].to==pa[x] || e[i].to==son[x]) continue;
            dfs2(e[i].to);
        }
        return ;
    }
    int LCA(int x,int y) {
        while(top[x]!=top[y]) {
            if(dpt[top[x]]<dpt[top[y]]) swap(x,y);
            x=pa[top[x]];
        }
        if(dpt[x]<dpt[y]) swap(x,y);
        return y;
    }
    int query(int x,int y) {
        using Segment_Tree :: query;
        using Segment_Tree :: root;
        int ans=0;
        while(top[x]!=top[y]) {
            if(dpt[top[x]]<dpt[top[y]]) swap(x,y);
            ans+=query(root[time_clock],pos[top[x]],pos[x]);
            x=pa[top[x]];
        }
        if(dpt[x]<dpt[y]) swap(x,y);
        ans+=query(root[time_clock],pos[y],pos[x]);
        return ans;
    }
    void change(int x,int y,int a0,int d) {
        using Segment_Tree :: root;
        using Segment_Tree :: change;
        tot_time++;
        root[tot_time]=root[time_clock];
        time_clock=tot_time;
        int lca=LCA(x,y);
        int an=a0+d*(dpt[x]+dpt[y]-2*dpt[lca]);
        while(top[x]!=top[y]) {
            if(dpt[top[x]]>dpt[top[y]]) {
                change(root[time_clock],pos[top[x]],pos[x],a0+d*(dpt[x]-dpt[top[x]]),-d);
                a0+=(dpt[x]-dpt[top[x]]+1)*d;
                x=pa[top[x]];
            }
            else {
                change(root[time_clock],pos[top[y]],pos[y],an-d*(dpt[y]-dpt[top[y]]),d);
                an-=(dpt[y]-dpt[top[y]]+1)*d;
                y=pa[top[y]];
            }
        }
        if(dpt[x]>dpt[y]) change(root[time_clock],pos[y],pos[x],an,-d);
        else change(root[time_clock],pos[x],pos[y],a0,d);
        return ;
    }
}
#undef int
int main() {
    #define int long long
    int n,m;
    scanf("%lld%lld",&n,&m);
    using namespace Tree_Partition;
    memset(fir,-1,sizeof fir);
    for(int i=1;i<n;i++) {
        int x,y;
        scanf("%lld%lld",&x,&y);
        Add_Edge(x,y);
    }
    dfs1(1), dfs2(1);
    Segment_Tree :: init(Segment_Tree :: root[0],1,n);
    int last_ans=0;
    while(m--) {
        char mode[5];
        int x,y,a0,d;
        scanf("%s",mode);
        if(mode[0]=='c') {
            scanf("%lld%lld%lld%lld",&x,&y,&a0,&d);
            x^=last_ans, y^=last_ans;
            change(x,y,a0,d);
        }
        else if(mode[0]=='q') {
            scanf("%lld%lld",&x,&y);
            x^=last_ans, y^=last_ans;
            printf("%lld\n",last_ans=query(x,y));
        }
        else {
            scanf("%lld",&x);
            x^=last_ans;
            time_clock=x;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值