题目链接
思路
树链剖分板子题。
比普通的DFS序多维护了点东西。
下面是一些概念
- 重儿子:对于每一个非叶子节点,它的儿子中 儿子数量最多的那一个儿子 为该节点的重儿子
- 轻儿子:对于每一个非叶子节点,它的儿子中 非重儿子 的剩下所有儿子即为轻儿子
- 叶子节点没有重儿子也没有轻儿子(因为它没有儿子。。)
- 重边:连接任意两个重儿子的边叫做重边
- 轻边:剩下的即为轻边
- 重链:相邻重边连起来的 连接一条重儿子 的链叫重链
- 对于叶子节点,若其为轻儿子,则有一条以自己为起点的长度为1的链
- 每一条重链以轻儿子为起点
还是跑DFS序,但是优先跑重儿子的,那么重链的DFS序是连续的,所以可以用各种数据结构维护。一条重链直接维护,不是一条重链的,用类似LCA的方法向上跳就好。
所以要DFS两次维护几个东西。
第一次DFS维护,每点深度,父亲,重儿子,子树节点数。
第二次维护,权值、下标的映射,每点所属重链的顶点。
那么每次类似lca跳,就可以跳到每条重链顶点的父亲就好,中间瞎搞下。
复杂度
树中任意两个节点之间的路径中轻边的条数不会超过
log
2
n
\log _{2}n
log2n ,重路径的数目不会超过
log
2
n
\log _{2}n
log2n,证明看别人的.
对两点的链维护或求值都是
O
(
l
o
g
n
)
O(logn)
O(logn),当然没算其他数据结构复杂度。
代码
#include <bits/stdc++.h>
using namespace std;
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
const int N = 100005;
vector<int> e[N];
int n, mod, _w[N], w[N];
/* 线段树 */
int sum[N<<2], lazy[N<<2];
void Build(int rt, int l, int r)
{
if(l == r){sum[rt] = w[l]%mod; return;}
int mid = l+r >> 1;
if(l <= mid) Build(lson);
if(r > mid) Build(rson);
sum[rt] = sum[rt<<1]+sum[rt<<1|1];
// printf("%d %d -- %d\n",l,r,sum[rt]);
}
void pushdown(int rt, int len)
{
if(lazy[rt])
{
sum[rt<<1] += lazy[rt]*(len-(len>>1));
sum[rt<<1|1] += lazy[rt]*(len>>1);
lazy[rt<<1] += lazy[rt];
lazy[rt<<1|1] += lazy[rt];
sum[rt<<1] %= mod;
sum[rt<<1|1] %= mod;
lazy[rt<<1] %= mod;
lazy[rt<<1|1] %= mod;
lazy[rt] = 0;
}
}
int query(int rt, int l, int r, int ql, int qr)
{
if(ql <= l && r <= qr) return sum[rt];
int mid = l+r >> 1;
pushdown(rt,r-l+1);
if(qr <= mid) return query(lson,ql,qr);
if(ql > mid) return query(rson,ql,qr);
return (query(lson,ql,qr)+query(rson,ql,qr))%mod;
}
void updata(int rt, int l, int r, int ql, int qr, int num)
{
if(ql <= l && r <= qr)
{
sum[rt] += (r-l+1)*num;
sum[rt] %= mod;
lazy[rt] += num;
lazy[rt] %= mod;
return;
}
pushdown(rt,r-l+1);
int mid = l+r>>1;
if(ql <= mid) updata(lson, ql, qr, num);
if(qr > mid) updata(rson, ql, qr, num);
sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}
/* END */
/* 树链剖分 */
int deep[N], f[N], siz[N], son[N]; // dfs1处理深度,父亲,重儿子,子树节点数
int cnt, id[N], top[N]; // dfs2 处理映射,权值,id,每点所属重链顶点
// 初始化保证 cnt = 0, son = 0, deep[0] = 0
void dfs1(int u, int fa)
{
deep[u] = deep[fa]+1;
f[u] = fa;
siz[u] = 1;
son[u] = 0;
int maxson = -1;
for(auto v : e[u])
{
if(v == fa) continue;
dfs1(v,u);
siz[u] += siz[v];
if(siz[v] > maxson) son[u] = v, maxson = siz[v];
}
}
void dfs2(int u, int topfa)
{
id[u] = ++cnt;
w[cnt] = _w[u];
top[u] = topfa;
if(!son[u]) return;
dfs2(son[u],topfa);
for(auto v : e[u])
{
if(v == son[u] || v == f[u]) continue;
dfs2(v,v);
}
}
void updatatree(int x, int y, int num)
{
num %= mod;
while(top[x] != top[y])
{
if(deep[top[x]] < deep[top[y]]) swap(x,y);
updata(1,1,n,id[top[x]],id[x],num);
x = f[top[x]];
}
if(deep[x] > deep[y]) swap(x,y);
updata(1,1,n,id[x],id[y],num);
}
int querytree(int x, int y)
{
int ans = 0;
while(top[x] != top[y])
{
if(deep[top[x]] < deep[top[y]]) swap(x,y);
ans += query(1,1,n,id[top[x]],id[x]);
ans %= mod;
x = f[top[x]];
}
if(deep[x] > deep[y]) swap(x,y);
ans += query(1,1,n,id[x],id[y]);
return ans%mod;
}
void updatason(int x, int num)
{
updata(1,1,n,id[x],id[x]+siz[x]-1,num);
}
int queryson(int x)
{
return query(1,1,n,id[x],id[x]+siz[x]-1);
}
/* END */
int main()
{
int m, r;
scanf("%d%d%d%d",&n,&m,&r,&mod);
for(int i = 1; i <= n; ++i) scanf("%d",&_w[i]);
for(int i = 2; i <= n; ++i)
{
int x, y;
scanf("%d%d",&x,&y);
e[x].push_back(y);
e[y].push_back(x);
}
dfs1(r,0);
dfs2(r,r);
Build(1,1,n);
while(m--)
{
int op, x, y, z;
scanf("%d",&op);
if(op == 1)
{
scanf("%d%d%d",&x,&y,&z);
updatatree(x,y,z);
}
else if(op == 2)
{
scanf("%d%d",&x,&y);
printf("%d\n",querytree(x,y));
}
else if(op == 3)
{
scanf("%d%d",&x,&y);
updatason(x,y);
}
else
{
scanf("%d",&x);
printf("%d\n",queryson(x));
}
// for(int i = 1; i <= n; ++i) printf("%d ",querytree(i,i)); printf(" *** \n");
}
return 0;
}