题目大意
1.首先我们看一下操作1:实际上可以说成在所有位置上加上
w
−
d
i
s
t
(
x
,
y
)
w-dist(x,y)
w−dist(x,y),因为
d
i
s
t
(
x
,
x
)
=
0
dist(x,x)=0
dist(x,x)=0
2.我们知道
d
i
s
t
(
x
,
y
)
=
d
e
p
[
x
]
+
d
e
p
[
y
]
−
2
∗
d
e
p
[
l
c
a
(
x
,
y
)
]
dist(x,y)=dep[x]+dep[y]-2*dep[lca(x,y)]
dist(x,y)=dep[x]+dep[y]−2∗dep[lca(x,y)]
3.代入化简
w
−
d
e
p
[
x
]
−
d
e
p
[
y
]
+
2
∗
d
e
p
[
l
c
a
(
x
,
y
)
]
w-dep[x]-dep[y]+2*dep[lca(x,y)]
w−dep[x]−dep[y]+2∗dep[lca(x,y)]
我们将这个式子拆开来看:
(1):
w
−
d
e
p
[
x
]
w-dep[x]
w−dep[x]
(2):
d
e
p
[
y
]
dep[y]
dep[y]
(3):
2
∗
d
e
p
[
l
c
a
(
x
,
y
)
]
2*dep[lca(x,y)]
2∗dep[lca(x,y)]
对于式子1,这是所有点都要加的,我们可以用一个全局变量统计一下
a
n
s
1
+
=
w
−
d
e
p
[
x
]
ans1+=w-dep[x]
ans1+=w−dep[x]
对于式子2是一个定值:为了计算减了几次我们用
n
u
m
num
num统计操作1的次数
对于式子3
x
与
所
有
的
y
的
L
C
A
都
是
x
到
根
节
点
1
【
假
设
的
】
路
径
上
的
点
x与所有的y的LCA都是x到根节点1【假设的】路径上的点
x与所有的y的LCA都是x到根节点1【假设的】路径上的点
如何查询答案呢?
关键是式子3:
这时候我们就要用到树剖去维护了
加上
2
∗
d
e
p
[
l
c
a
(
x
,
y
)
]
2*dep[lca(x,y)]
2∗dep[lca(x,y)]实际上就是相当于给
1
−
>
l
c
a
的
路
径
上
边
权
加
2
1->lca的路径上边权加2
1−>lca的路径上边权加2
由于对于所有点的lca都是一条从
1
−
>
x
1->x
1−>x的路径,所有修改就是在
1
−
>
x
1->x
1−>x链上面每个点
+
2
+2
+2
假如你要查询y点的值:就是
a
n
s
1
−
n
u
m
∗
d
e
p
[
y
]
+
q
u
e
r
y
(
1
,
y
)
;
ans1-num*dep[y]+query(1,y);
ans1−num∗dep[y]+query(1,y);
这里为什么是直接
q
u
e
r
y
(
1
,
y
)
query(1,y)
query(1,y)呢?
因为每次我们只是修改中心点到根节点的链上面的信息如果你y点到根节点路径和这个相交的话交点就是LCA,交点下面是0,交点上面是修改过的,所以直接询问query(1,y);
操作2相当于对这个点额外减掉一些值:开个ad数组维护一下就好了
#include <iostream>
#include <cstdio>
#include <stack>
#include <sstream>
#include <limits.h>
#include <vector>
#include <map>
#include <cstring>
#include <deque>
#include <cmath>
#include <iomanip>
#include <queue>
#include <algorithm>
#include <set>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define _for(i,a,b) for( int i = (a); i < (b); ++i)
#define _rep(i,a,b) for( int i = (a); i <= (b); ++i)
#define for_(i,a,b) for( int i = (a); i >= (b); -- i)
#define rep_(i,a,b) for( int i = (a); i > (b); -- i)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define hash Hash
#define next Next
#define count Count
#define pb push_back
#define f first
#define s second
using namespace std;
const int N = 3e5+10, mod = 1e9 + 9;
const long double eps = 1e-5;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x)
{
x = 0;char ch = getchar();ll f = 1;
while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args)
{
read(first);
read(args...);
}
int T;
int n, m;
struct node {
int to, next;
}e[N];
int head[N],cnt;
//.................................建图
inline void add(int from, int to)
{
e[cnt] = {to,head[from]};
head[from] = cnt ++;
}
//..................................
ll fa[N], dep[N], siz[N], son[N];
//每个点的父节点,深度,子树大小,重儿子是谁
inline void dfs1(int u, int f)
{
fa[u] = f, dep[u] = dep[f] + 1, siz[u] = 1;
int max_size = -1;
for(int i = head[u]; ~i; i = e[i].next)
{
int v = e[i].to;
if(v == f) continue;
dfs1(v,u);
siz[u] += siz[v];
if(siz[v] > max_size)
{
son[u] = v;
max_size = siz[v];
}
}
}
//..................................
ll tim, dfn[N], top[N];//dfs序,重链顶部
inline void dfs2(int u, int t) // 节点重链的顶部
{
dfn[u] = ++ tim, top[u] = t;
if(!son[u]) return;
dfs2(son[u],t);//先跑重链
for(int i = head[u]; ~i; i = e[i].next)
{
int v = e[i].to;
if(v == fa[u] || v == son[u]) continue;
dfs2(v,v);
}
}
//....................................线段树
ll tr[N << 2], tag[N << 2];
inline void pushup(int rt)
{
tr[rt] = (tr[rt << 1] + tr[rt << 1|1]);
}
inline void pushdown(int rt, int l, int r)
{
if(tag[rt])
{
tag[rt << 1] += tag[rt];
tag[rt << 1|1] += tag[rt];
tr[rt << 1] = (tr[rt << 1] + 1ll * (mid - l + 1) * tag[rt]);
tr[rt << 1|1] = (tr[rt << 1|1] + 1ll * (r - mid) * tag[rt]);
tag[rt] = 0;
}
}
inline void build(int rt, int l , int r)
{
if(l == r)
{
tr[rt] = 0;
return;
}
build(Lson), build(Rson);
pushup(rt);
}
inline void modify(int rt, int l, int r, int posl, int posr, int val)
{
if(posl <= l && r <= posr)
{
tag[rt] += val;
tr[rt] = (tr[rt] + 1ll * (r - l + 1) * val);
return;
}
pushdown(rt, l, r);
if(posl <= mid) modify(Lson,posl,posr,val);
if(posr > mid) modify(Rson,posl,posr,val);
pushup(rt);
}
inline ll query(int rt, int l, int r, int posl, int posr)
{
if(posl <= l && posr >= r) return tr[rt];
pushdown(rt,l,r);
ll res = 0;
if(posl <= mid) res = (res + query(Lson,posl, posr));
if(posr > mid) res = (res + query(Rson,posl,posr));
return res;
}
//....................................树剖链上操作
inline void mchain(int x, int y, ll z)
{
while(top[x] != top[y])
{
if(dep[top[x]] < dep[top[y]]) swap(x,y);
modify(1,1,n,dfn[top[x]],dfn[x],z);
x = fa[top[x]];
}
if(dep[x] > dep[y]) swap(x,y);
modify(1,1,n,dfn[x],dfn[y],z);
}
inline ll qchain(int x, int y)
{
ll res = 0;
while(top[x] != top[y])
{
if(dep[top[x]] < dep[top[y]]) swap(x,y);
res += query(1,1,n,dfn[top[x]],dfn[x]);
x = fa[top[x]];
}
if(dep[x] > dep[y]) swap(x,y);
res += query(1,1,n,dfn[x],dfn[y]);
return res;
}
//...........................................
ll ad[N];
ll num, ans1;
//............................................
inline void init()
{
num = ans1 = cnt = tim = 0;
ms(ad,0), ms(head,-1), ms(tag,0);
ms(son,0);
dep[1] = 0;
}
int main()
{
read(T);
while(T --)
{
init();
read(n,m);
_for(i,0,n-1)
{
int l, r;
read(l,r);
add(l,r);
add(r,l);
}
dfs1(1,1);
dfs2(1,1);
build(1,1,n);
while(m --)
{
int op,x,y;
read(op);
if(op == 1)
{
read(x,y);
num ++;
ans1 += y - dep[x];
mchain(1,x,2);
}
else if(op == 2)
{
read(y);
int v = ans1 - num * dep[y] + qchain(1,y);
if(v > ad[y]) ad[y] = v;
}
else
{
read(y);
printf("%lld\n",ans1 - num * dep[y] + qchain(1,y)-ad[y]);
}
}
}
return 0;
}