洛谷P3384 【模板】树链剖分
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 \leq 10, M \leq 10N≤10,M≤10
对于70%的数据: N \leq {10}^3, M \leq {10}^3N≤103,M≤103
对于100%的数据: N \leq {10}^5, M \leq {10}^5N≤105,M≤105
( 其实,纯随机生成的树LCA+暴力是能过的,可是,你觉得可能是纯随机的么233 )
样例说明:
树的结构如下:
各个操作如下:
故输出应依次为2、21(重要的事情说三遍:记得取模)
#include<iostream> #include<cstdio> #include<algorithm> #define N 100010 using namespace std; struct node{int to,pre;}e[N*2]; struct Tree{ int v,lazy; int l,r; }tr[N*3]; int dep[N],sz[N],son[N],fa[N],top[N],w[N]; int wt[N],id[N],head[N]; int n,m,root,mod,ne,res=0,cnt,num; void Insert(int from,int to){ e[++num].to=to; e[num].pre=head[from]; head[from]=num; } void dfs1(int now,int father){ dep[now]=dep[father]+1; fa[now]=father; sz[now]=1; for(int i=head[now];i;i=e[i].pre){ int to=e[i].to; if(to==father)continue; dfs1(to,now); sz[now]+=sz[to]; if(sz[to]>sz[son[now]])son[now]=to; } } void dfs2(int now,int topfather){ id[now]=++cnt; wt[cnt]=w[now]; top[now]=topfather; if(!son[now])return; dfs2(son[now],topfather); for(int i=head[now];i;i=e[i].pre){ int to=e[i].to; if(to==fa[now]||to==son[now])continue; dfs2(to,to); } } void build(int k,int l,int r){ tr[k].l=l;tr[k].r=r; if(l==r){ tr[k].v=wt[l]%mod; return; } int mid=(l+r)>>1; build(k<<1,l,mid);build(k<<1|1,mid+1,r); tr[k].v=(tr[k<<1].v+tr[k<<1|1].v)%mod; } void down(int k){ int vv=tr[k].lazy; tr[k].lazy=0; tr[k<<1].lazy+=vv;tr[k<<1|1].lazy+=vv; tr[k<<1].v=(tr[k<<1].v+vv*(tr[k<<1].r-tr[k<<1].l+1))%mod; tr[k<<1|1].v=(tr[k<<1|1].v+vv*(tr[k<<1|1].r-tr[k<<1|1].l+1))%mod; } void update(int k,int opl,int opr,int opv){ if(tr[k].l>=opl&&tr[k].r<=opr){ tr[k].lazy=(tr[k].lazy+opv)%mod; tr[k].v=(tr[k].v+opv*(tr[k].r-tr[k].l+1))%mod; return; } down(k); int mid=(tr[k].l+tr[k].r)>>1; if(opl<=mid)update(k<<1,opl,opr,opv); if(opr>mid)update(k<<1|1,opl,opr,opv); tr[k].v=(tr[k<<1].v+tr[k<<1|1].v)%mod; } void change1(int x,int y,int opv){ opv%=mod; while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); update(1,id[top[x]],id[x],opv); x=fa[top[x]]; } if(dep[x]>dep[y])swap(x,y); update(1,id[x],id[y],opv); } void query(int k,int opl,int opr){ if(tr[k].l>=opl&&tr[k].r<=opr){ res+=tr[k].v;res%=mod; return; } down(k); int mid=(tr[k].l+tr[k].r)>>1; if(opl<=mid)query(k<<1,opl,opr); if(opr>mid)query(k<<1|1,opl,opr); } int query1(int x,int y){ int ans=0; while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); res=0; query(1,id[top[x]],id[x]); ans+=res;ans%=mod; x=fa[top[x]]; } if(dep[x]>dep[y])swap(x,y); res=0; query(1,id[x],id[y]); ans+=res;ans%=mod; return ans; } void change2(int x,int opv){ update(1,id[x],id[x]+sz[x]-1,opv); } int query2(int x){ res=0; query(1,id[x],id[x]+sz[x]-1); return res; } int main(){ scanf("%d%d%d%d",&n,&m,&root,&mod); for(int i=1;i<=n;i++)scanf("%d",&w[i]); int x,y,z; for(int i=1;i<n;i++){ scanf("%d%d",&x,&y); Insert(x,y),Insert(y,x); } dep[0]=-1; dfs1(root,0);dfs2(root,root); build(1,1,n); while(m--){ int op; scanf("%d",&op); if(op==1){ scanf("%d%d%d",&x,&y,&z); change1(x,y,z); } else if(op==2){ scanf("%d%d",&x,&y); cout<<query1(x,y)<<endl; } else if(op==3){ scanf("%d%d",&x,&z); change2(x,z); } else { scanf("%d",&x); cout<<query2(x)<<endl; } } return 0; }