Luogu-P3384 【模板】轻重链剖分/树链剖分
作者:互联网
学习
是看这篇博文学习的。
https://www.cnblogs.com/chinhhh/p/7965433.html
代码
#include <bits/stdc++.h>
using namespace std;
#define MAXN 100005
long long Ha;
int n,m,root;
vector<int> graph[MAXN];
int siz[MAXN];
int son[MAXN];
int fa[MAXN];
int dep[MAXN];
int top[MAXN];
int id[MAXN];
int cnt;
int rk[MAXN];
long long w[MAXN];
struct segment_tree {
#define LX (X<<1)
#define RX ((X<<1)|1)
static const int _MAXN=MAXN;
struct node {
long long s;
int l,r,len;
long long tag;
} a[_MAXN<<3];
void build(int X, int l, int r) {
a[X].l=l;
a[X].r=r;
a[X].len=r-l+1;
a[X].tag=0;
if (l==r) {
a[X].s=w[rk[l]];
return;
}
build(LX, l, (l+r)>>1);
build(RX, ((l+r)>>1)+1, r);
a[X].s=a[LX].s+a[RX].s;
a[X].s%=Ha;
}
void down_tag(int X) {
a[LX].s+=a[X].tag*a[LX].len %Ha, a[LX].s%=Ha;
a[LX].tag+=a[X].tag, a[LX].tag%=Ha;
a[RX].s+=a[X].tag*a[RX].len %Ha, a[RX].s%=Ha;
a[RX].tag+=a[X].tag, a[RX].tag%=Ha;
a[X].tag=0;
}
void modify_add(int X, int L, int R, int V) {
if (a[X].l>=L && a[X].r<=R) {
a[X].s+=V*a[X].len %Ha; a[X].s%=Ha;
a[X].tag+=V; a[X].tag%=Ha;
return;
}
down_tag(X);
if (a[LX].r>=L) modify_add(LX, L, R, V);
if (a[RX].l<=R) modify_add(RX, L, R, V);
a[X].s=(a[LX].s+a[RX].s) %Ha;
}
long long query_sum(int X, int L, int R) {
if (a[X].l>=L && a[X].r<=R) {
return a[X].s;
}
long long ret=0;
down_tag(X);
if (a[LX].r>=L) ret+=query_sum(LX, L, R);
if (a[RX].l<=R) ret+=query_sum(RX, L, R);
return ret%Ha;
}
} SegT;
//第一次dfs,计算 fa\dep\size\son
void dfs1(int X, int Fa) //(当前节点,父节点)
{
fa[X]=Fa;
dep[X]=dep[Fa]+1;
siz[X]=1;
int mxsiz=0,mxX=0;
for (int &o: graph[X]) if (o!=Fa) {
dfs1(o, X);
siz[X]+=siz[o];
if (siz[o]>mxsiz) {
mxsiz=siz[o];
mxX=o;
}
}
son[X]=mxX;
}
//第二次dfs,计算 top\id\rk
void dfs2(int X, int Fa, int T) //(当前节点,父节点,所在链的顶端节点)
{
top[X]=T;
id[X]=++cnt;
rk[cnt]=X;
if (son[X])
dfs2(son[X], X, T); //重儿子
for (int &o: graph[X]) if (o!=Fa && o!=son[X])
dfs2(o, X, o); //普通儿子
}
void init()
{
dfs1(root,0);
dfs2(root,0,root);
SegT.build(1,0,n);
}
int main()
{
//freopen("P3384_2.in","r",stdin);
scanf("%d%d%d%lld",&n,&m,&root,&Ha);
for (int i=1; i<=n; i++) scanf("%lld",&w[i]);
for (int i=1,uu,vv; i<n; i++) {
scanf("%d%d",&uu,&vv);
graph[uu].push_back(vv);
graph[vv].push_back(uu);
}
init();
for (int ttt=1,opr,x,y,z; ttt<=m; ttt++) {
scanf("%d",&opr);
if (opr==1) { //路径加
scanf("%d%d%d",&x,&y,&z);
while (top[x]!=top[y]) {
if (dep[top[x]]<dep[top[y]]) swap(x,y); //让 x 为 top 更深的那个点
SegT.modify_add(1, id[top[x]], id[x], z); //跳上去,区间加
x=fa[top[x]];
}
//此时 xy 处于同一条链上,路径 x->y 为一段连续区间
if (dep[x]>dep[y]) swap(x,y);
SegT.modify_add(1, id[x], id[y], z);
}
else if (opr==2) { //路径求和
scanf("%d%d",&x,&y);
long long ans=0;
while (top[x]!=top[y]) {
if (dep[top[x]]<dep[top[y]]) swap(x,y);
ans+= SegT.query_sum(1, id[top[x]], id[x]);
ans%=Ha;
x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
ans+= SegT.query_sum(1, id[x],id[y]);
ans%=Ha;
printf("%lld\n",ans);
}
else if (opr==3) { //子树加
scanf("%d%d",&x,&z);
SegT.modify_add(1, id[x], id[x]+siz[x]-1, z);
}
else if (opr==4) { //子树求和
scanf("%d",&x);
printf("%lld\n",SegT.query_sum(1, id[x], id[x]+siz[x]-1));
}
}
return 0;
}
/*
4 6 1 998244353
0 0 2 4
2 1
3 1
4 1
3 3 1
1 2 3 1
2 1 1
4 1
1 1 4 6
2 3 1
8 10 2 448348
458 718 447 857 633 264 238 944
1 2
2 3
3 4
6 2
1 5
5 7
8 6
3 7 611
4 6
3 1 267
3 2 111
1 6 3 153
3 7 673
4 8
2 6 1
4 7
3 4 228
8 10 2 448348
0 0 0 0 0 0 0 0
1 2
2 3
3 4
6 2
1 5
5 7
8 6
3 7 1
4 6
3 1 1
3 2 1
1 6 3 1
3 7 1
4 8
2 6 1
4 7
3 4 1
*/
标签:链剖分,剖分,int,Luogu,long,tag,Ha,id,LX 来源: https://blog.csdn.net/jackypigpig/article/details/120283104