树链剖分
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){
laz[rt] = 0; 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++) g[i].clear();
memset(son, 0, sizeof(son)); 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)); } } }
https://cn.vjudge.net/contest/28982#problem/C
这个权值是在边上,每个边的权值算在这条边较深的那个点上,在跳重链的时候和权值在点上是一样的(结合图想一下,重链之间没有重复的点,所以跳到另一条链时直接可以是从top节点开始)
但是两个点在一条重链上时,就应该和权值在点上有所不同,因为只能算边,所以最上面那个点不能算。
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> using namespace std; typedef long long ll; typedef pair<int, int> pii; const int N = 10000+10; const int INF = 0x3f3f3f3f; struct Node { int v, w; Node(int v = 0, int w = 0) : v(v),w(w) {}; } node[N]; vector<Node> g[N]; int w[N],wt[N]; pii p[N]; //w[]初始的、wt[]新的点权数组 int laz[N<<2], ma[N << 2], mi[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] == -1) { laz[rt<<1]*=laz[rt]; laz[rt<<1|1]*=laz[rt]; ma[rt << 1] *= laz[rt]; mi[rt << 1] *= laz[rt]; swap(ma[rt << 1], mi[rt << 1]); ma[rt << 1 | 1] *= laz[rt]; mi[rt << 1 | 1] *= laz[rt]; swap(ma[rt << 1 | 1], mi[rt << 1 | 1]); laz[rt]=1; } } void pushup(int rt) { ma[rt] = max(ma[rt << 1], ma[rt << 1 | 1]); mi[rt] = min(mi[rt << 1], mi[rt << 1 | 1]); } void build(int rt,int l,int r){ laz[rt] = 1; if(l==r){ mi[rt]=wt[l]; ma[rt] = wt[l]; return; } int m = (l + r) >> 1; build(rt << 1, l, m); build(rt << 1 | 1, m + 1, r); pushup(rt); } void querymax(int rt, int l, int r, int le, int re) { if(le <= l && re >= r) { res = max(res, ma[rt]); return; } pushdown(rt, r - l + 1); int m = (l + r) >> 1; if(re <= m) querymax(rt << 1, l, m, le, re); else if(le > m) querymax(rt << 1 | 1, m + 1, r, le, re); else { querymax(rt << 1, l, m, le, m); querymax(rt << 1 | 1, m + 1, r, m + 1, re); } } void update(int rt,int l,int r,int le,int re){ if(le<=l&&re >= r){ laz[rt] = laz[rt] * (-1); ma[rt] = ma[rt] * (-1); mi[rt] = mi[rt] * (-1); swap(ma[rt], mi[rt]); return; } pushdown(rt,r - l + 1); int m = (l + r) >> 1; if(re <= m) update(rt << 1, l, m, le, re); else if(le > m) update(rt << 1 | 1, m + 1, r, le, re); else { update(rt << 1, l, m, le, m); update(rt << 1 | 1, m + 1, r, m + 1, re); } pushup(rt); } void updatepos(int rt, int l, int r, int pos, int k) { if(l == r) { ma[rt] = mi[rt] = k; return; } pushdown(rt, r - l + 1); int m = (l + r) >> 1; if(pos <= m) updatepos(rt << 1, l, m, pos, k); else updatepos(rt << 1 | 1, m + 1, r, pos, k); pushup(rt); } //---------------------------------以上为线段树 int qRange(int x,int y) { int ans=-INF; while(top[x]!=top[y]){//当两个点不在同一条链上 if(dep[top[x]]<dep[top[y]])swap(x,y);//把x点改为所在链顶端的深度更深的那个点 res=-INF; querymax(1,1,n,id[top[x]],id[x]);//ans加上x点到x所在链顶端 这一段区间的点权和 ans=max(ans, res); x=fa[top[x]];//把x跳到x所在链顶端的那个点的上面一个点 } //直到两个点处于一条链上 if(x==y) return ans; if(dep[x]>dep[y])swap(x,y);//把x点深度更深的那个点 res=-INF; querymax(1,1,n,id[son[x] ],id[y]);//这时再加上此时两个点的区间和即可 ans=max(ans, res); return ans; } void updRange(int x,int y){//同上 while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); update(1,1,n,id[top[x]],id[x]); x=fa[top[x]]; } if(x == y) return; if(dep[x]>dep[y])swap(x,y); update(1,1,n,id[son[x] ],id[y]); } 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].v; int ww=g[x][i].w; if(y==f)continue;//若为父亲则continue dfs1(y,x,deep+1);//dfs其儿子 w[y]=ww; 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].v; if(y==fa[x]||y==son[x])continue; dfs2(y,y);//对于每一个轻儿子都有一条从它自己开始的链 } } int t; int main(){ scanf("%d", &t); while(t--) { scanf("%d", &n); int u, v, w; for(int i = 1; i <= n; i++) g[i].clear(); memset(son, 0, sizeof(son)); for(int i=1;i<n;i++){ scanf("%d%d%d", &u, &v, &w); p[i] = pii(u, v); g[u].push_back(Node(v, w)); g[v].push_back(Node(u, w)); } cnt = 0; dfs1(1,0,1); dfs2(1,1); build(1,1,n); char ch[10]; while(~scanf("%s", ch)){ int x, y; if(ch[0] == 'Q') { scanf("%d%d", &x, &y); printf("%d\n",qRange(x, y)); } else if(ch[0] == 'C') { scanf("%d%d", &x, &y); if(dep[p[x].first] < dep[p[x].second]) { swap(p[x].first, p[x].second); } updatepos(1, 1, n, id[p[x].first ], y); } else if(ch[0] == 'N'){ scanf("%d%d", &x, &y); updRange(x, y); } else break; } } }