树链剖分套树状数组(区间修改)板子
Posted Stump
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树链剖分套树状数组(区间修改)板子相关的知识,希望对你有一定的参考价值。
P3384 【模板】树链剖分
题目描述
如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作:
操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z
操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和
操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z
操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和
输入输出格式
输入格式:
第一行包含4个正整数N、M、R、P,分别表示树的结点个数、操作个数、根节点序号和取模数(即所有的输出结果均对此取模)。
接下来一行包含N个非负整数,分别依次表示各个节点上初始的数值。
接下来N-1行每行包含两个整数x、y,表示点x和点y之间连有一条边(保证无环且连通)
接下来M行每行包含若干个正整数,每行表示一个操作,格式如下:
操作1: 1 x y z
操作2: 2 x y
操作3: 3 x z
操作4: 4 x
输出格式:
输出包含若干行,分别依次表示每个操作2或操作4所得的结果(对P取模)
输入输出样例
说明
时空限制:1s,128M
数据规模:
对于30%的数据:N≤10,M≤10
对于70%的数据: N≤103,M≤103
对于100%的数据: N≤105,M≤105
( 其实,纯随机生成的树LCA+暴力是能过的,可是,你觉得可能是纯随机的么233 )
样例说明:
树的结构如下:
各个操作如下:
故输出应依次为2、21(重要的事情说三遍:记得取模)
赛前提高一下码题能力.
吐槽一下,写了1hour的树剖【吐血】.
链接:https://www.luogu.org/problemnew/show/3384
AC代码:
#include<cstdio> #include<iostream> #define FOR(i,s,t) for(register int i=s;i<=t;++i) typedef long long ll; const int maxn=100011; int n,m,r,p,type,x,y; ll z; int a[maxn]; #define VIS(now) for(register int e=las[now];e;e=nxt[e]) namespace TCP_And_BIT{ ll tr1[maxn],tr2[maxn],num[maxn]; inline int lowbit(int x){ return x&(-x); } inline void add(ll *r,int pos,ll x){ for(;pos<=n;r[pos]+=x,r[pos]%=p,pos+=lowbit(pos));return; } inline void _add(int l,int r,ll x){ x%=p; add(tr1,l,x);add(tr1,r+1,(-x+p)%p); add(tr2,l,1ll*(l-1)*x%p);add(tr2,r+1,1ll*(p-x)%p*r); } inline ll query(ll *r,int pos){ ll ans=0;for(;pos;ans+=r[pos],ans%=p,pos-=lowbit(pos));return ans; } inline ll _query(int l,int r){ ll sum1,sum2; sum1=1ll*(l-1)*query(tr1,l-1)-1ll*query(tr2,l-1); sum2=1ll*r*query(tr1,r)-1ll*query(tr2,r); return (sum2-sum1+2*p)%p; } int nxt[maxn<<1],las[maxn],to[maxn<<1]; int dep[maxn],sz[maxn],f[maxn],top[maxn],xu[maxn]; int tot,dfn; inline void insert(int x,int y){ nxt[++tot]=las[x];las[x]=tot;to[tot]=y;return; } inline void dfs1(int now){ sz[now]=1; VIS(now) if(!dep[to[e]]){ dep[to[e]]=dep[now]+1;f[to[e]]=now; dfs1(to[e]);sz[now]+=sz[to[e]]; } return; } inline void dfs2(int now,int chain){ xu[now]=++dfn; num[dfn]=a[now]; top[now]=chain; add(tr1,dfn,1ll*(num[dfn]-num[dfn-1]+p)%p); add(tr2,dfn,1ll*(dfn-1)%p*(num[dfn]-num[dfn-1]+p)%p); register int i=0; VIS(now)if(to[e]!=f[now]&&sz[i]<sz[to[e]])i=to[e]; if(!i)return; dfs2(i,chain); VIS(now)if(to[e]!=f[now]&&i!=to[e])dfs2(to[e],to[e]); return; } inline ll query_sum(int x,int y){ ll ans=0; while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])x^=y^=x^=y; ans+=_query(xu[top[x]],xu[x]);ans=(ans+2*p)%p;x=f[top[x]]; } if(dep[x]<dep[y])x^=y^=x^=y; ans+=_query(xu[y],xu[x]);ans=(ans+2*p)%p; return ans; } inline void add_num(int x,int y,ll v){ while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])x^=y^=x^=y; _add(xu[top[x]],xu[x],v);x=f[top[x]]; } if(dep[x]<dep[y])x^=y^=x^=y; _add(xu[y],xu[x],v);return ; } }; using namespace TCP_And_BIT; int main(){ scanf("%d%d%d%d",&n,&m,&r,&p); FOR(i,1,n)scanf("%d",a+i),a[i]%=p; FOR(i,2,n){ scanf("%d%d",&x,&y); insert(x,y);insert(y,x); } dep[r]=1; dfs1(r);dfs2(r,r); while(m--){ scanf("%d",&type); if(type==1){ scanf("%d%d%lld",&x,&y,&z); z%=p; add_num(x,y,z); } if(type==2){ scanf("%d%d",&x,&y); printf("%lld\n",(query_sum(x,y)+p)%p); } if(type==3){ scanf("%d%lld",&x,&z); z%=p; _add(xu[x],xu[x]+sz[x]-1,z); } if(type==4){ scanf("%d",&x); printf("%lld\n",(_query(xu[x],xu[x]+sz[x]-1)+p)%p); } } return 0; }
以上是关于树链剖分套树状数组(区间修改)板子的主要内容,如果未能解决你的问题,请参考以下文章
POJ 2763 Housewife Wind(树链剖分+树状数组)