标签:clock long 一段 range nbsp 递归 continue const upd
https://www.cnblogs.com/chinhhh/p/7965433.html#dfs1
树链剖分跳链logn的复杂度。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N =200000+10; vector<int> g[N]; int w[N],wt[N]; //w[]初始的、wt[]新的点权数组 int a[N<<2],laz[N<<2]; //线段树数组、lazy操作 int son[N],id[N],fa[N],cnt,dep[N],siz[N],top[N]; //son[]重儿子编号,id[]新编号,fa[]父亲节点,cnt dfs_clock/dfs序,dep[]深度,siz[]子树大小,top[]当前链顶端节点 int res; //查询答案 int n,m,r,mod; //-------------------------------------- 以下为线段树 inline void pushdown(int rt,int len){ if(laz[rt]) { laz[rt<<1]+=laz[rt]; laz[rt<<1|1]+=laz[rt]; a[rt<<1]+=laz[rt]*(len-(len>>1)); a[rt<<1|1]+=laz[rt]*(len>>1); a[rt<<1]%=mod; a[rt<<1|1]%=mod; laz[rt]=0; } } void pushup(int rt) { a[rt]=(a[rt<<1]+a[rt<<1|1])%mod; } void build(int rt,int l,int r){ if(l==r){ a[rt]=wt[l]; if(a[rt]>mod)a[rt]%=mod; return; } int m = (l + r) >> 1; build(rt << 1, l, m); build(rt << 1 | 1, m + 1, r); pushup(rt); } void query(int rt,int l,int r,int le,int re){ if(le<=l&&r<=re){ res+=a[rt]; res%=mod; return; } pushdown(rt,r - l + 1); int m = (l + r) >> 1; if(re <= m) query(rt << 1, l, m, le, re); else if(le > m) query(rt << 1 | 1, m + 1, r, le, re); else { query(rt << 1, l, m, le, m); query(rt << 1 | 1, m + 1, r, m + 1, re); } } void update(int rt,int l,int r,int le,int re,int k){ if(le<=l&&re >= r){ laz[rt]+=k; a[rt]+=k*(r - l + 1); if(a[rt] > mod) a[rt] %= mod; return; } pushdown(rt,r - l + 1); int m = (l + r) >> 1; if(re <= m) update(rt << 1, l, m, le, re, k); else if(le > m) update(rt << 1 | 1, m + 1, r, le, re, k); else { update(rt << 1, l, m, le, m, k); update(rt << 1 | 1, m + 1, r, m + 1, re, k); } pushup(rt); } //---------------------------------以上为线段树 int qRange(int x,int y){ int ans=0; while(top[x]!=top[y]){//当两个点不在同一条链上 if(dep[top[x]]<dep[top[y]])swap(x,y);//把x点改为所在链顶端的深度更深的那个点 res=0; query(1,1,n,id[top[x]],id[x]);//ans加上x点到x所在链顶端 这一段区间的点权和 ans+=res; ans%=mod;//按题意取模 x=fa[top[x]];//把x跳到x所在链顶端的那个点的上面一个点 } //直到两个点处于一条链上 if(dep[x]>dep[y])swap(x,y);//把x点深度更深的那个点 res=0; query(1,1,n,id[x],id[y]);//这时再加上此时两个点的区间和即可 ans+=res; return ans%mod; } void updRange(int x,int y,int k){//同上 k%=mod; while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); update(1,1,n,id[top[x]],id[x],k); x=fa[top[x]]; } if(dep[x]>dep[y])swap(x,y); update(1,1,n,id[x],id[y],k); } int qSon(int x){ res=0; query(1,1,n,id[x],id[x]+siz[x]-1);//子树区间右端点为id[x]+siz[x]-1 return res; } void updSon(int x,int k){//同上 k %= mod; update(1,1,n,id[x],id[x]+siz[x]-1,k); } void dfs1(int x,int f,int deep){//x当前节点,f父亲,deep深度 dep[x]=deep;//标记每个点的深度 fa[x]=f;//标记每个点的父亲 siz[x]=1;//标记每个非叶子节点的子树大小 int maxson=-1;//记录重儿子的儿子数 int len = g[x].size(); for(int i = 0; i < len; i++){ int y=g[x][i]; if(y==f)continue;//若为父亲则continue dfs1(y,x,deep+1);//dfs其儿子 siz[x]+=siz[y];//把它的儿子数加到它身上 if(siz[y]>maxson)son[x]=y,maxson=siz[y];//标记每个非叶子节点的重儿子编号 } } void dfs2(int x,int topf){//x当前节点,topf当前链的最顶端的节点 id[x]=++cnt;//标记每个点的新编号 wt[cnt]=w[x];//把每个点的初始值赋到新编号上来 top[x]=topf;//这个点所在链的顶端 if(!son[x])return;//如果没有儿子则返回 dfs2(son[x],topf);//按先处理重儿子,再处理轻儿子的顺序递归处理 int len = g[x].size(); for(int i = 0; i < len; i++){ int y=g[x][i]; if(y==fa[x]||y==son[x])continue; dfs2(y,y);//对于每一个轻儿子都有一条从它自己开始的链 } } int main(){ scanf("%d%d%d%d", &n, &m, &r, &mod); for(int i=1;i<=n;i++) scanf("%d", &w[i]); int u, v; for(int i=1;i<n;i++){ scanf("%d%d", &u, &v); g[u].push_back(v); g[v].push_back(u); } cnt = 0; dfs1(r,0,1); dfs2(r,r); build(1,1,n); while(m--){ int k,x,y,z; scanf("%d", &k); if(k==1){ scanf("%d%d%d", &x, &y, &z); updRange(x,y,z); } else if(k==2){ scanf("%d%d", &x, &y); printf("%d\n",qRange(x,y)); } else if(k==3){ scanf("%d%d", &x, &y); updSon(x,y); } else{ scanf("%d", &x); printf("%d\n",qSon(x)); } } }
标签:clock long 一段 range nbsp 递归 continue const upd
原文地址:https://www.cnblogs.com/downrainsun/p/11373882.html