树链剖分模板
摘要:
本以为自己学会了树剖,但最后发现我还是只会用树剖求lca,至于最后的线段树。。。
完全没有用到top,fa,son数组!
也就是说我学的树剖是错的!
所以赶紧来补一波板子:
题目:
这是一道模板题。
给定一棵n个节点的树,初始时该树的根为 1 号节点,每个节点有一个给定的权值。下面依次进行 m 个操作,操作分为如下五种类型:
-
换根:将一个指定的节点设置为树的新根。
-
修改路径权值:给定两个节点,将这两个节点间路径上的所有节点权值(含这两个节点)增加一个给定的值。
-
修改子树权值:给定一个节点,将以该节点为根的子树内的所有节点权值增加一个给定的值。
-
询问路径:询问某条路径上节点的权值和。
-
询问子树:询问某个子树内节点的权值和。
输入格式
第一行一个整数n,表示节点的个数。
第二行n个整数表示第i个节点的初始权值$a_i$。
第三行n-1个整数,表示i+1号节点的父节点编号$f_{i+1}(1\leqslant f_{i+1}\leqslant n)$。
第四行一个整数m,表示操作个数。
接下来m行,每行第一个整数表示操作类型编号:$1\leqslant u,v\leqslant n$
若类型为1,则接下来一个整数u,表示新根的编号。
若类型为2,则接下来三个整数u,v,k,分别表示路径两端的节点编号以及增加的权值。
若类型为3,则接下来两个整数u,k,分别表示子树根节点编号以及增加的权值。
若类型为4,则接下来两个整数u,v,表示路径两端的节点编号。
若类型为5,则接下来一个整数u,表示子树根节点编号。
输出格式
对于每一个类型为4或5的操作,输出一行一个整数表示答案。
样例
样例输入
6
1 2 3 4 5 6
1 2 1 4 4
6
4 5 6
2 2 4 1
5 1
1 4
3 1 2
4 2 5
样例输出
15
24
19
数据范围与提示
对于 100% 的数据,$1\leqslant n,m,k,a_i\leqslant 10^5$
数据有一定梯度。
有不同板子:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#define Rint register int
#define mem(a,b) memset(a,(b),sizeof(a))
#define Temp template<typename T>
using namespace std;
typedef long long LL;
Temp inline void read(T &x){
x=0;T w=1,ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar();
x=x*w;
}
#define mid ((l+r)>>1)
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define len (r-l+1)
const int maxn=200000+10;
int n,m,r,mod;
//见题意
int e,beg[maxn],nex[maxn],to[maxn],w[maxn],wt[maxn];
//链式前向星数组,w[]、wt[]初始点权数组
int a[maxn<<2],laz[maxn<<2];
//线段树数组、lazy操作
int son[maxn],id[maxn],fa[maxn],cnt,dep[maxn],siz[maxn],top[maxn];
//son[]重儿子编号,id[]新编号,fa[]父亲节点,cnt dfs_clock/dfs序,dep[]深度,siz[]子树大小,top[]当前链顶端节点
int res=0;
//查询答案
inline void add(int x,int y){//链式前向星加边
to[++e]=y;
nex[e]=beg[x];
beg[x]=e;
}
//-------------------------------------- 以下为线段树
inline void pushdown(int rt,int lenn){
laz[rt<<1]+=laz[rt];
laz[rt<<1|1]+=laz[rt];
a[rt<<1]+=laz[rt]*(lenn-(lenn>>1));
a[rt<<1|1]+=laz[rt]*(lenn>>1);
a[rt<<1]%=mod;
a[rt<<1|1]%=mod;
laz[rt]=0;
}
inline void build(int rt,int l,int r){
if(l==r){
a[rt]=wt[l];
if(a[rt]>mod)a[rt]%=mod;
return;
}
build(lson);
build(rson);
a[rt]=(a[rt<<1]+a[rt<<1|1])%mod;
}
inline void query(int rt,int l,int r,int L,int R){
if(L<=l&&r<=R){res+=a[rt];res%=mod;return;}
else{
if(laz[rt])pushdown(rt,len);
if(L<=mid)query(lson,L,R);
if(R>mid)query(rson,L,R);
}
}
inline void update(int rt,int l,int r,int L,int R,int k){
if(L<=l&&r<=R){
laz[rt]+=k;
a[rt]+=k*len;
}
else{
if(laz[rt])pushdown(rt,len);
if(L<=mid)update(lson,L,R,k);
if(R>mid)update(rson,L,R,k);
a[rt]=(a[rt<<1]+a[rt<<1|1])%mod;
}
}
//---------------------------------以上为线段树
inline 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;
}
inline 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);
}
inline int qSon(int x){
res=0;
query(1,1,n,id[x],id[x]+siz[x]-1);//子树区间右端点为id[x]+siz[x]-1
return res;
}
inline void updSon(int x,int k){//同上
update(1,1,n,id[x],id[x]+siz[x]-1,k);
}
inline void dfs1(int x,int f,int deep){//x当前节点,f父亲,deep深度
dep[x]=deep;//标记每个点的深度
fa[x]=f;//标记每个点的父亲
siz[x]=1;//标记每个非叶子节点的子树大小
int maxson=-1;//记录重儿子的儿子数
for(Rint i=beg[x];i;i=nex[i]){
int y=to[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];//标记每个非叶子节点的重儿子编号
}
}
inline 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);//按先处理重儿子,再处理轻儿子的顺序递归处理
for(Rint i=beg[x];i;i=nex[i]){
int y=to[i];
if(y==fa[x]||y==son[x])continue;
dfs2(y,y);//对于每一个轻儿子都有一条从它自己开始的链
}
}
int main(){
read(n);read(m);read(r);read(mod);
for(Rint i=1;i<=n;i++)read(w[i]);
for(Rint i=1;i<n;i++){
int a,b;
read(a);read(b);
add(a,b);add(b,a);
}
dfs1(r,0,1);
dfs2(r,r);
build(1,1,n);
while(m--){
int k,x,y,z;
read(k);
if(k==1){
read(x);read(y);read(z);
updRange(x,y,z);
}
else if(k==2){
read(x);read(y);
printf("%d\n",qRange(x,y));
}
else if(k==3){
read(x);read(y);
updSon(x,y);
}
else{
read(x);
printf("%d\n",qSon(x));
}
}
}
还有个链接:https://www.cnblogs.com/chinhhh/p/7965433.html#build
这个:(倍增)
#include<cstdio>
#include<vector>
#include<algorithm>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define ll long long
using namespace std;
const int N=1e5+5;
int son[N],siz[N],dep[N],top[N],idx[N],rev[N],num;
int lg[N],f[N][20];
ll sum[N<<2],add[N<<2];
int n,q,op,a,b,c,w[N],root=1;
vector<int>G[N];
struct TREE{
inline void pushup(int rt) {sum[rt]=sum[rt<<1]+sum[rt<<1|1];}
inline void build(int l,int r,int rt)
{
add[rt]=0;
if(l==r) {sum[rt]=w[rev[l]];return;}
int m=(l+r)>>1;
build(lson),build(rson);
pushup(rt);
}
inline void pushdown(int rt,int m)
{
if(add[rt])
{
add[rt<<1]+=add[rt],add[rt<<1|1]+=add[rt];
sum[rt<<1]+=add[rt]*(m-(m>>1));
sum[rt<<1|1]+=add[rt]*(m>>1);
add[rt]=0;
}
}
inline void update(int L,int R,int val,int l,int r,int rt)
{
if(L<=l && r<=R)
{
sum[rt]+=(ll)val*(r-l+1);
add[rt]+=val;
return;
}
pushdown(rt,r-l+1);
int m=(l+r)>>1;
if(L<=m) update(L,R,val,lson);
if(R>m) update(L,R,val,rson);
pushup(rt);
}
inline ll query(int L,int R,int l,int r,int rt)
{
if(L<=l && r<=R) return sum[rt];
pushdown(rt,r-l+1);
int m=(l+r)>>1;ll ans=0;
if(L<=m) ans+=query(L,R,lson);
if(R>m) ans+=query(L,R,rson);
return ans;
}
}T;
struct Tree{
inline void dfs1(int x)
{
dep[x]=dep[f[x][0]]+1,siz[x]=1;
for(int i=1;(1<<i)<=dep[x];++i) f[x][i]=f[f[x][i-1]][i-1];
for(int i=0;i<(int)G[x].size();++i)
{
int v=G[x][i];
if(v==f[x][0]) continue;
dfs1(v),siz[x]+=siz[v];
if(!son[x] || siz[v]>siz[son[x]]) son[x]=v;
}
}
inline void dfs2(int x,int t)
{
top[x]=t,idx[x]=++num,rev[num]=x;
if(son[x]) dfs2(son[x],t);
for(int i=0;i<(int)G[x].size();++i)
{
int v=G[x][i];
if(v==f[x][0] || v==son[x]) continue;
dfs2(v,v);
}
}
inline int lca(int x, int y)
{
if(dep[x]<dep[y]) swap(x, y);
for(int i=18;i>=0;--i)
if(dep[f[x][i]]>dep[y]) x=f[x][i];
if(f[x][0]==y) return x;
if(dep[f[x][0]]>=dep[y]) x=f[x][0];
for(int i=lg[dep[x]]-1;i>=0;--i)
if(f[x][i]^f[y][i]) x=f[x][i],y=f[y][i];
return x;
}
inline void update_subtree(int x,int z)
{
if(x==root) {T.update(1,n,z,1,n,1);return;}
int d=lca(x,root);
// printf("test:%d %d %d\n",x,root,d);
if(f[d][0]==x) T.update(1,n,z,1,n,1),T.update(idx[d],idx[d]+siz[d]-1,-z,1,n,1);
else T.update(idx[x],idx[x]+siz[x]-1,z,1,n,1);
}
inline ll query_subtree(int x)
{
if(x==root) return T.query(1,n,1,n,1);
int d=lca(x,root);
// printf("test2:%d %d %d\n",x,root,d);
if(f[d][0]==x) return T.query(1,n,1,n,1)-T.query(idx[d],idx[d]+siz[d]-1,1,n,1);
else return T.query(idx[x],idx[x]+siz[x]-1,1,n,1);
}
inline void update_path(int x,int y,int z)
{
while(top[x]^top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
T.update(idx[top[x]],idx[x],z,1,n,1);
x=f[top[x]][0];
}
if(dep[x]>dep[y]) swap(x,y);
T.update(idx[x],idx[y],z,1,n,1);
}
inline ll query_path(int x,int y)
{
ll ans=0;
while(top[x]^top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
ans+=T.query(idx[top[x]],idx[x],1,n,1);
x=f[top[x]][0];
}
if(dep[x]>dep[y]) swap(x,y);
ans+=T.query(idx[x],idx[y],1,n,1);
return ans;
}
}t;
int main()
{
// freopen("tree1.in","r",stdin);
// freopen("tree.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",&w[i]),lg[i]=lg[i>>1]+1;
for(int i=2;i<=n;++i)
scanf("%d",&f[i][0]),
G[f[i][0]].push_back(i),G[i].push_back(f[i][0]);
scanf("%d",&q);
t.dfs1(1),t.dfs2(1,1),T.build(1,n,1);
while(q--)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d",&a);
root=a;
}
if(op==2)
{
scanf("%d%d%d",&a,&b,&c);
t.update_path(a,b,c);
}
if(op==3)
{
scanf("%d%d",&a,&b);
t.update_subtree(a,b);
}
if(op==4)
{
scanf("%d%d",&a,&b);
printf("%lld\n",t.query_path(a,b));
}
if(op==5)
{
scanf("%d",&a);
printf("%lld\n",t.query_subtree(a));
}
// printf("root:%d\n",root);
}
return 0;
}
还有:(努力想找一个和自己码风相似的)
#include <cstdio>
#include <algorithm>
using namespace std;
#define lson(x) (x << 1)
#define rson(x) (x << 1 | 1)
#define int long long
const int maxn = 500050;
struct Edge {
int to, nxt;
} edge[maxn << 1];
struct point {
int val, add;
} e[maxn << 2];
int n, q, root = 1, ecnt, cnt;
int head[maxn], a[maxn], b[maxn];
int par[maxn], size[maxn], dep[maxn], son[maxn], id[maxn], top[maxn];
namespace segment_tree {
void build(int x, int l, int r) {
e[x].add = 0;
if (l == r)
e[x].val = a[l];
else {
int mid = (l + r) >> 1;
build(lson(x), l, mid);
build(rson(x), mid + 1, r);
e[x].val = e[lson(x)].val + e[rson(x)].val;
}
}
void pushdown(int x, int l, int r) {
int mid = (l + r) >> 1;
e[lson(x)].val += (mid - l + 1) * e[x].add;
e[rson(x)].val += (r - mid) * e[x].add;
e[lson(x)].add += e[x].add;
e[rson(x)].add += e[x].add;
e[x].add = 0;
}
void update_addtag(int x, int stdl, int stdr, int l, int r, int k) {
if (r < stdl || stdr < l)
return;
if (l <= stdl && stdr <= r) {
e[x].val += k * (stdr - stdl + 1);
e[x].add += k;
return;
}
pushdown(x, stdl, stdr);
int mid = (stdl + stdr) >> 1;
update_addtag(lson(x), stdl, mid, l, r, k);
update_addtag(rson(x), mid + 1, stdr, l, r, k);
e[x].val = e[lson(x)].val + e[rson(x)].val;
}
int query(int x, int stdl, int stdr, int l, int r) {
if (r < stdl || stdr < l)
return 0;
if (l <= stdl && stdr <= r)
return e[x].val;
pushdown(x, stdl, stdr);
int mid = (stdl + stdr) >> 1;
return query(lson(x), stdl, mid, l, r) + query(rson(x), mid + 1, stdr, l, r);
}
} // namespace segment_tree
using namespace segment_tree;
namespace hcp {
void add(int x, int y) {
edge[++ecnt].to = y;
edge[ecnt].nxt = head[x];
head[x] = ecnt;
}
void dfs1(int x) {
size[x] = 1;
dep[x] = dep[par[x]] + 1;
for (int i = head[x]; i; i = edge[i].nxt) {
int t = edge[i].to;
if (t == par[x])
continue;
par[t] = x;
dfs1(t);
size[x] += size[t];
if (!son[x] || size[son[x]] < size[t])
son[x] = t;
}
}
void dfs2(int x, int y) {
top[x] = y;
id[x] = ++cnt, a[cnt] = b[x];
if (son[x])
dfs2(son[x], y);
for (int i = head[x]; i; i = edge[i].nxt) {
int t = edge[i].to;
if (t == par[x] || t == son[x])
continue;
dfs2(t, t);
}
}
int lca(int x, int y) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]])
swap(x, y);
x = par[top[x]];
}
if (dep[x] > dep[y])
swap(x, y);
return x;
}
int findson(int x, int root) {
while (top[root] != top[x]) {
if (par[top[root]] == x)
return top[root];
root = par[top[root]];
}
return son[x];
}
} // namespace hcp
using namespace hcp;
namespace solve_all {
void update_path(int x, int y, int val) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]])
swap(x, y);
update_addtag(1, 1, n, id[top[x]], id[x], val);
x = par[top[x]];
}
if (dep[x] > dep[y])
swap(x, y);
update_addtag(1, 1, n, id[x], id[y], val);
}
void update_subtree(int x, int val) {
if (lca(x, root) != x)
update_addtag(1, 1, n, id[x], id[x] + size[x] - 1, val);
else if (x == root)
update_addtag(1, 1, n, 1, n, val);
else {
int s = findson(x, root);
update_addtag(1, 1, n, 1, id[s] - 1, val);
if (id[s] + size[s] <= n)
update_addtag(1, 1, n, id[s] + size[s], n, val);
}
}
int query_path(int x, int y) {
int ans = 0;
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]])
swap(x, y);
ans += query(1, 1, n, id[top[x]], id[x]);
x = par[top[x]];
}
if (dep[x] > dep[y])
swap(x, y);
ans += query(1, 1, n, id[x], id[y]);
return ans;
}
int query_subtree(int x) {
int ans = 0;
if (lca(x, root) != x)
ans += query(1, 1, n, id[x], id[x] + size[x] - 1);
else if (x == root)
ans += query(1, 1, n, 1, n);
else {
int s = findson(x, root);
ans += query(1, 1, n, 1, id[s] - 1);
if (id[s] + size[s] <= n)
ans += query(1, 1, n, id[s] + size[s], n);
}
return ans;
}
} // namespace solve_all
using namespace solve_all;
signed main() {
scanf("%lld", &n);
for (int i = 1; i <= n; i++) scanf("%lld", &b[i]);
for (int i = 2; i <= n; i++) {
int x;
scanf("%lld", &x);
add(x, i), add(i, x);
}
dfs1(1);
dfs2(1, 1);
build(1, 1, n);
scanf("%lld", &q);
while (q--) {
int opt, x, y, z;
scanf("%lld", &opt);
if (opt == 1)
scanf("%lld", &root);
if (opt == 2)
scanf("%lld%lld%lld", &x, &y, &z), update_path(x, y, z);
if (opt == 3)
scanf("%lld%lld", &x, &y), update_subtree(x, y);
if (opt == 4)
scanf("%lld%lld", &x, &y), printf("%lld\n", query_path(x, y));
if (opt == 5)
scanf("%lld", &x), printf("%lld\n", query_subtree(x));
}
return 0;
}
还有用树状数组的:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100005
int n, m, root = 1, a[N];
int head[N];
struct edgeType {
int to, next;
} edge[N << 1];
inline void addEdge(int from, int to) {
static int cnt = 0;
edge[++cnt] = (edgeType){ to, head[from] };
head[from] = cnt;
}
class BinaryIndexedTree {
private:
long long c[2][N];
inline int lowbit(int x) { return x & (-x); }
inline void update(int x, long long v) {
for (int i = x; i <= n; i += lowbit(i)) c[0][i] += v, c[1][i] += x * v;
}
inline long long query(int x) {
long long ans = 0;
for (int i = x; i > 0; i -= lowbit(i)) ans += (x + 1) * c[0][i] - c[1][i];
return ans;
}
public:
inline void update(int l, int r, long long v) {
update(l, v);
update(r + 1, -v);
}
inline long long query(int l, int r) { return query(r) - query(l - 1); }
} BIT;
class TreeChain {
private:
int root, parent[N], size[N], depth[N], son[N], top[N], tid[N], ted[N];
inline void dfsPre(int u, int p, int d) {
parent[u] = p;
depth[u] = d;
size[u] = 1;
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if (v == p)
continue;
dfsPre(v, u, d + 1);
size[u] += size[v];
if (size[v] > size[son[u]])
son[u] = v;
}
}
inline void dfsTop(int u, int tp) {
static int dfsClock = 0;
top[u] = tp;
tid[u] = ++dfsClock;
if (son[u])
dfsTop(son[u], tp);
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if (v == parent[u] || v == son[u])
continue;
dfsTop(v, v);
}
ted[u] = dfsClock;
}
inline int reach(int x, int y) {
int ans = 0;
while (top[x] != top[y]) {
ans = top[x];
x = parent[top[x]];
}
return x == y ? ans : son[y];
}
public:
inline void init(int rt) {
root = rt;
dfsPre(root, 0, 1);
dfsTop(root, root);
}
inline void changeRoot(int rt) { root = rt; }
inline void updateInit(int x, int v) { BIT.update(tid[x], tid[x], v); }
inline void updateRoute(int x, int y, int z) {
while (top[x] != top[y]) {
if (depth[top[x]] < depth[top[y]])
std::swap(x, y);
BIT.update(tid[top[x]], tid[x], z);
x = parent[top[x]];
}
if (depth[x] > depth[y])
std::swap(x, y);
BIT.update(tid[x], tid[y], z);
}
inline long long queryRoute(int x, int y) {
long long ans = 0;
while (top[x] != top[y]) {
if (depth[top[x]] < depth[top[y]])
std::swap(x, y);
ans += BIT.query(tid[top[x]], tid[x]);
x = parent[top[x]];
}
if (depth[x] > depth[y])
std::swap(x, y);
return ans + BIT.query(tid[x], tid[y]);
}
inline void updateSubtree(int x, int z) {
if (x == root)
BIT.update(1, n, z);
else if (tid[x] <= tid[root] && ted[x] >= ted[root]) {
int t = reach(root, x);
BIT.update(1, tid[t] - 1, z);
BIT.update(ted[t] + 1, n, z);
} else
BIT.update(tid[x], ted[x], z);
}
inline long long querySubtree(int x) {
if (x == root)
return BIT.query(1, n);
else if (tid[x] <= tid[root] && ted[x] >= ted[root]) {
int t = reach(root, x);
return BIT.query(1, tid[t] - 1) + BIT.query(ted[t] + 1, n);
} else
return BIT.query(tid[x], ted[x]);
}
};
TreeChain TC;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 2; i <= n; i++) {
int x;
scanf("%d", &x);
addEdge(i, x);
addEdge(x, i);
}
TC.init(1);
for (int i = 1; i <= n; i++) TC.updateInit(i, a[i]);
scanf("%d", &m);
for (int i = 1; i <= m; i++) {
int opt, x, y, z;
scanf("%d%d", &opt, &x);
if (opt == 1)
TC.changeRoot(x);
else if (opt == 2) {
scanf("%d%d", &y, &z);
TC.updateRoute(x, y, z);
} else if (opt == 3) {
scanf("%d", &y);
TC.updateSubtree(x, y);
} else if (opt == 4) {
scanf("%d", &y);
printf("%lld\n", TC.queryRoute(x, y));
} else {
printf("%lld\n", TC.querySubtree(x));
}
}
return 0;
}

浙公网安备 33010602011771号