标签:push 深度 efi space oid sum ring 返回 二次
参考https://www.cnblogs.com/wushengyang/p/10808505.html,感谢
#include<iostream> #include<algorithm> #include<cstring> #include<bits/stdc++.h> using namespace std; const int maxn=100005; #define ll long long int #define ls l,mid,rt<<1 #define rs mid+1,r,rt<<1|1 int N,M,R,P,cnt,head[maxn<<1]; using namespace std; struct edge { int to,next; } e[maxn<<1]; void add(int x,int y) { e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt; } //记录子树中点的数量(包括自己) int size[maxn]; //节点在树中的深度 int deep[maxn]; //节点的父节点 int fa[maxn]; //节点的重儿子 int son[maxn]; // u代表节点,f代表父亲,dep代表深度 void dfs1(int u,int f,int dep) { //当前只有自己 size[u]=1; //深度 deep[u]=dep; //父节点 fa[u]=f; //遍历子节点 for(int i=head[u]; i; i=e[i].next) { //如果是父节点,就跳过 if(e[i].to==fa[u]) continue; //遍历子节点 dfs1(e[i].to,u,dep+1); //子树中点的数量 size[u]+=size[e[i].to]; //更新重儿子 if(size[e[i].to]>size[son[u]]) son[u]=e[i].to; } } int tim; //节点所在重链上的起始儿子 int top[maxn]; //节点的dfs序 int tid[maxn]; //dfs序对应的绩点 int rank[maxn]; //u代表节点 ,t代表u所在重链的根部 void dfs2(int u,int t) { //记录重链的起始儿子 top[u]=t; //更新dfs序 tid[u]=++tim; //dfs序对应的节点 rank[tim]=u; //如果到了叶节点,就返回 if(son[u]==-1) return ; //将该点的重儿子,重重儿子,重重重儿子等等连成一条重链 dfs2(son[u],t); //更新轻儿子上的重儿子 for(int i=head[u]; i; i=e[i].next) { //如果v不是u的重儿子,就构造新的重链 if(e[i].to!=fa[u]&&e[i].to!=son[u]) dfs2(e[i].to,e[i].to); } } //建立线段树 // 和 懒标记 ll sum[maxn<<2],lazy[maxn<<2],a[maxn]; void pushup(int rt) { sum[rt]=(sum[rt<<1]+sum[rt<<1|1])%P; } void build(int l,int r,int rt) { //根据dfs序来建树而不是 sum[rt]=a[l]; if(l==r) { sum[rt]=a[rank[l]]%P; return ; } int mid=(l+r)/2; build(ls); build(rs); pushup(rt); } void pushdown(int rt,int ln,int rn) { if(lazy[rt]) { lazy[rt<<1]=(lazy[rt<<1]+lazy[rt])%P; lazy[rt<<1|1]=(lazy[rt<<1|1]+lazy[rt])%P; sum[rt<<1]=(sum[rt<<1]+lazy[rt]*ln%P)%P; sum[rt<<1|1]=(sum[rt<<1|1]+lazy[rt]*rn%P)%P; lazy[rt]=0; } } // 要求改的区间,加的值域,从1到N,根节点 void update(int L,int R,int c,int l,int r,int rt) { if(L<=l&&R>=r) { sum[rt]+=c*(r-l+1); lazy[rt]+=c; return ; } int mid=(l+r)/2; pushdown(rt,mid-l+1,r-mid); if(L<=mid) update(L,R,c,l,mid,rt<<1); if(R>mid) update(L,R,c,mid+1,r,rt<<1|1); pushup(rt); } //线段树中的查询 ll query(int L,int R,int l,int r,int rt) { if(L<=l&R>=r) return sum[rt]; int mid=(l+r)/2; pushdown(rt,mid-l+1,r-mid); ll ans=0; if(L<=mid) ans=(ans+query(L,R,ls)%P)%P; if(R>mid) ans=(ans+query(L,R,rs)%P)%P; return ans%P; } //奖节点x到y路径上所有节点的值增加c // void update1(int x,int y,int c) { //当不在同一条重链 while(top[x]!=top[y]) { //更新重链起始深度较大的 if(deep[top[x]]<deep[top[y]]) swap(x,y); //更新大的 //从重链起始点到当前点,都是按dfs序 //增加c //从1到N找 //从根节点 update(tid[top[x]],tid[x],c,1,N,1); //x变为所在重链起点的父节点, x=fa[top[x]]; } //此时已经在一条重链上 //还是排序深度 if(deep[x]<deep[y]) swap(x,y); //更新 update(tid[y],tid[x],c,1,N,1); } //查询 ll query1(int x,int y) { ll ans=0; while(top[x]!=top[y]) { if(deep[top[x]]<deep[top[y]]) swap(x,y); //一样的思路 //先加所在重链的, ans=(ans+query(tid[top[x]],tid[x],1,N,1)%P)%P; //然后往上移动 x=fa[top[x]]; } //当在一套重链的时候 if(deep[x]<deep[y]) swap(x,y); //再加 ans+=query(tid[y],tid[x],1,N,1)%P; return ans%P; } int main() { cin>>N>>M>>R>>P; //初始化,默认重儿子都是-1 for(int i=0; i<=N; i++) son[i]=-1; //输入每个点的值 for(int i=1; i<=N; i++) cin>>a[i]; int x,y; //建边 for(int i=1; i<N; i++) { cin>>x>>y; add(x,y); add(y,x); } //第一遍dfs //求出节点的子树数量(size),节点在树中的深度(deep),节点的父亲节点(fa),节点的重儿子(son) dfs1(R,0,1); //第二次dfs //则是求出每个节点所在的重链上的起始重儿子是谁,节点是第几次被dfs到的(dfs序),dfs序所对应的节点 dfs2(R,R); //根据第二次dfs求出的新编号建立线段树 build(1,N,1); int op,z; for(int i=1; i<=M; i++) { cin>>op; //将树从x到y结点最短路径上所有节点的值都加上z if(op==1) { cin>>x>>y>>z; update1(x,y,z); } //求树从x到y结点最短路径上所有节点的值之和 if(op==2) { cin>>x>>y; cout<<query1(x,y)<<endl; } //以x为根节点的子树内所有节点值都加上z if(op==3) { cin>>x>>z; update(tid[x],tid[x]+size[x]-1,z,1,N,1); } //以x为根节点的子树内所有节点值之和 if(op==4) { cin>>x; //按dfs序查 , cout<<query(tid[x],tid[x]+size[x]-1,1,N,1)%P<<endl; } } return 0; }
标签:push 深度 efi space oid sum ring 返回 二次
原文地址:https://www.cnblogs.com/QingyuYYYYY/p/12312150.html