暑假集训 ---- (树上)数据结构

ZJOI 2007 捉迷藏
一棵树,点权为 0/1,每次修改一个点的点权,询问两个最远的 1 的距离
点分树每个点维护:到当前节点的最远距离,维护最大和次大拼起来的,和一个全局最大的

#include<bits/stdc++.h>
#define N 200050
#define inf 0x3fffffff
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
int n, q, a[N], cnt;
int first[N], nxt[N], to[N], tot;
void add(int x, int y){ nxt[++tot] = first[x], first[x] = tot, to[tot] = y;}
int father[N]; // 点分树
int fa[N][20], dep[N];
int Siz, rt, Maxson[N], siz[N], vis[N];
struct Node{
	priority_queue<int> q1, q2;
	void Push(int x){ q1.push(x); }
	void Delete(int x){ q2.push(x); }
	int Top(){
		while(q2.size() && q1.top() == q2.top()) q1.pop(), q2.pop();
		return q1.top();
	}
	void Pop(){
		while(q2.size() && q1.top() == q2.top()) q1.pop(), q2.pop();
		q1.pop();
	}
	int Top2(){ int val = Top(); Pop(); int ret = Top(); Push(val); return ret;}
	int Siz(){ return q1.size() - q2.size();}
}h1[N], h2[N], h3;
void dfs(int u, int f){
	for(int i=1; i<=18; i++)
		fa[u][i] = fa[fa[u][i-1]][i-1];
	for(int i=first[u];i;i=nxt[i]){
		int t = to[i]; if(t == f) continue;
		dep[t] = dep[u] + 1; fa[t][0] = u; dfs(t, u);
	}
}
void getrt(int u, int f){
	siz[u] = 1; Maxson[u] = 0;
	for(int i=first[u];i;i=nxt[i]){
		int t = to[i]; if(t == f || vis[t]) continue;
		getrt(t, u); siz[u] += siz[t];
		Maxson[u] = max(Maxson[u], siz[t]);
	} Maxson[u] = max(Maxson[u], Siz - Maxson[u]);
	if(Maxson[u] < Maxson[rt]) rt = u;
}
int lca(int x, int y){
	if(dep[x] < dep[y]) swap(x, y);
	for(int i=18; i>=0; i--) if(dep[fa[x][i]] >= dep[y]) x = fa[x][i];
	if(x == y) return x;
	for(int i=18; i>=0; i--) if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
	return fa[x][0];
}
int Dis(int x, int y){ return dep[x] + dep[y] - 2 * dep[lca(x, y)];}
void dfs2(int u, int fa, int goal){
	h1[rt].Push(Dis(u, goal));
	for(int i=first[u];i;i=nxt[i]){
		int t = to[i]; if(t == fa || vis[t]) continue;
		dfs2(t, u, goal);
	}
}
void Divide(int u, int fa){
	father[u] = fa; vis[u] = 1; h2[u].Push(0);
	int sum = Siz;
	for(int i=first[u];i;i=nxt[i]){
		int t = to[i]; if(vis[t]) continue;
		if(siz[t] > siz[u]) Siz = sum - siz[u];
		else Siz = siz[t];
		rt = 0; getrt(t, 0);
		dfs2(rt, 0, u);
		h2[u].Push(h1[rt].Top());
		Divide(rt, u);
	}
	if(h2[u].Siz() > 1) h3.Push(h2[u].Top() + h2[u].Top2());
}
void off(int x){
	if(h2[x].Siz() > 1) h3.Delete(h2[x].Top() + h2[x].Top2());
	h2[x].Delete(0);
	if(h2[x].Siz() > 1) h3.Push(h2[x].Top() + h2[x].Top2());
	#define fa father[u]
	for(int u = x; fa; u = fa){ 
		if(h2[fa].Siz() > 1) h3.Delete(h2[fa].Top() + h2[fa].Top2());
		h2[fa].Delete(h1[u].Top());
		h1[u].Delete(Dis(x, fa));
		if(h1[u].Siz()) h2[fa].Push(h1[u].Top());
		if(h2[fa].Siz() > 1) h3.Push(h2[fa].Top() + h2[fa].Top2()); 
	} 
}
void on(int x){
	if(h2[x].Siz() > 1) h3.Delete(h2[x].Top() + h2[x].Top2());
	h2[x].Push(0);
	if(h2[x].Siz() > 1) h3.Push(h2[x].Top() + h2[x].Top2());
	for(int u = x; fa; u = fa){
		if(h2[fa].Siz() > 1) h3.Delete(h2[fa].Top() + h2[fa].Top2());
		if(h1[u].Siz()) h2[fa].Delete(h1[u].Top());
		h1[u].Push(Dis(x, fa));
		h2[fa].Push(h1[u].Top());
		if(h2[fa].Siz() > 1) h3.Push(h2[fa].Top() + h2[fa].Top2());
	}
	#undef fa
}
int main(){
	cnt = n = read();
	for(int i=1; i<n; i++){
		int x = read(), y = read(); 
		add(x, y); add(y, x);
	} dep[1] = 1; dfs(1, 0);
	Maxson[0] = inf; Siz = n; getrt(1, 0); 
	Divide(rt, 0);
	q = read(); 
	while(q--){
		char op[3]; scanf("%s", op);
		if(op[0] == 'C'){
			int x = read();
			if(a[x]) cnt++, on(x);
			else cnt--, off(x);
			a[x] ^= 1;
		}
		else{
			if(cnt < 2) printf("%d\n", cnt - 1);
			else printf("%d\n", h3.Top());
		}
	} return 0;
} 

BZOJ 2566 xmastree
题意:一棵有边权的树,结点有颜色,每次修改一个点的颜色,求每次修改后所有同色结点的最近距离
类似上一道题,每个颜色用一个 m u l t i s e t multiset multiset 分开维护就可以了
BZOJ3730 震波
建出点分树后可以每个点维护一个树状数组, 表示 u 子树中到 u 距离为 1 / 2 / 3... 1/2/3 ... 1/2/3... 的点的权值和
那么 对于u 的子树, ≤ k \le k k 的树状数组查一下就可以了。
但这样是算重了的,因为可能到了一个分治中心又走回自己的子树,其实就是正常点分时的差分
于是再维护一个树状数组表示 u 子树中到 fa 距离为 1 / 2 / 3... 1/2/3... 1/2/3... 的点权和
树状数组可以用 vector 动态开点, 这样空间是 n l o g n nlogn nlogn


#include<bits/stdc++.h>
#define N 500050
using namespace std;
int first[N], nxt[N], to[N], tot;
void add(int x, int y){
	nxt[++tot] = first[x], first[x] = tot, to[tot] = y;
}
int n, m, val[N];
struct vec{
	vector<int> v; int s;
	void init(){ v.assign(s+10, 0);}
	void update(int x, int val){
		x++; for(;x<=s;x+=x&-x) v[x]+=val;
	}
	int Ask(int x){ 
		x++; x = min(x, s); int res = 0;
		for(;x>=1;x-=x&-x) res += v[x]; return res;
	}
}f1[N], f2[N];
int st[N][22], sign, id[N], dis[N], lg[N];
void dfs(int u, int f){
	st[++sign][0] = dis[u]; id[u] = sign;
	for(int i=first[u];i;i=nxt[i]){
		int t = to[i]; if(t == f) continue;
		dis[t] = dis[u] + 1; dfs(t, u);
		st[++sign][0] = dis[u]; 
	}
}
int siz[N], Maxson[N], rt, Siz, vis[N];
void getrt(int u, int f){
	siz[u] = 1; Maxson[u] = 0;
	for(int i=first[u];i;i=nxt[i]){
		int t = to[i]; if(t == f || vis[t]) continue;
		getrt(t, u); siz[u] += siz[t];
		Maxson[u] = max(Maxson[u], siz[t]);
	} Maxson[u] = max(Maxson[u], Siz - siz[u]);
	if(Maxson[u] < Maxson[rt]) rt = u;
}
int fa[N], dep[N], d[N], res;
int dist(int u, int v){
	int x = id[u], y = id[v];
	if(x > y) swap(x, y);
	int t = lg[y - x + 1];
	return dis[u] + dis[v] - 2 * min(st[x][t], st[y-(1<<t)+1][t]);
}
void getdis(int u, int f){
	d[dep[u]] += val[u]; res = max(res, dep[u]);
	for(int i=first[u];i;i=nxt[i]){ 
		int t = to[i]; if(t == f || vis[t]) continue;
		dep[t] = dep[u] + 1; getdis(t, u);
	} 
}
void calc(vec &x){
	x.s = res + 1; x.init();
	for(int i=0; i<=res; i++){
		x.update(i, d[i]); d[i] = 0;
	} res = 0; 
}
void Divide(int u, int f){
	fa[u] = f; vis[u] = 1; dep[u] = 0; getdis(u, 0); 
	calc(f1[u]); int sum = Siz;
	for(int i=first[u];i;i=nxt[i]){
		int t = to[i]; if(vis[t]) continue;
		if(siz[t] > siz[u]) Siz = sum - siz[u];
		else Siz = siz[t];
		rt = 0; getrt(t, 0); dep[t] = 1; getdis(t, 0);
		calc(f2[rt]); dep[t] = 0; Divide(rt, u);
	}
}
int ans;
int Qu(int x, int k){
	int res = 0;
	for(int i=x; i; i = fa[i]){
		res += f1[i].Ask(k - dist(i, x));
	}
	for(int i=x; fa[i]; i = fa[i]){
		res -= f2[i].Ask(k - dist(fa[i], x));
	} return res;
}
void Modify(int x, int k){	
	for(int i=x; i; i = fa[i]){
		f1[i].update(dist(x, i), k);
	}
	for(int i=x; fa[i]; i = fa[i]){
		f2[i].update(dist(fa[i], x), k);
	}
}
int main(){
	scanf("%d%d", &n, &m);
	for(int i=1; i<=n; i++) scanf("%d", &val[i]);
	for(int i=1; i<n; i++){
		int x, y; scanf("%d%d", &x, &y);
		add(x, y); add(y, x);
	} dfs(1, 0);
	for(int i=2; i<=sign; i++) lg[i] = lg[i>>1] + 1;
	for(int i=1; (1<<i) <= sign; i++)
		for(int j=1; j+(1<<i)-1 <= sign; j++)
			st[j][i] = min(st[j][i-1], st[j+(1<<(i-1))][i-1]);
	Siz = n; Maxson[0] = 1e9; 
	getrt(1, 0); 
	Divide(rt, 0); 
	while(m--){
		int op, x, y; scanf("%d%d%d", &op, &x, &y); x ^= ans; y ^= ans;
		if(op == 0) ans = Qu(x, y), printf("%d\n", ans);
		if(op == 1) Modify(x, y - val[x]), val[x] = y;
	} return 0;

[ZJOI2015]幻想乡战略游戏
我们考虑假设当前最优点是x,如果修改了一个点权,就会想某个儿子偏移
我们可以枚举这个儿子(因为度数 ≤ 20 \le20 20), 然后计算在这个儿子的答案,如果比当前小就往儿子走
现在有两个问题:

  1. 如何快速计算在儿子的答案
  2. 往儿子走有可能被卡成 n 2 n^2 n2

发现计算一个点的答案,即 ∑ v i ∗ d i \sum v_i*d_i vidi可以用点分树,因为我们只需把 u , f a u . . . u,fa_u... u,fau... 统计一遍就可以得到答案
需要维护这些东西: s u m ( u ) = ∑ t ∈ s u b t r e e ( u ) v t sum(u)=\sum_{t\in subtree(u)}v_t sum(u)=tsubtree(u)vt s u m d ( u ) = ∑ t ∈ s u b t r e e ( u ) v t ∗ d i s ( t , u ) sumd(u)=\sum_{t\in subtree(u)}v_t*dis(t,u) sumd(u)=tsubtree(u)vtdis(t,u)
然后当前点的答案就是 ∑ s u m d ( f a ) + d i s ( u , f a ) ∗ s u m ( f a ) \sum sumd(fa)+dis(u,fa)*sum(fa) sumd(fa)+dis(u,fa)sum(fa)
类似上一道题也会算重,于是同样减一下父亲的贡献
对于第二个问题,我们可以在点分树上走,并不是一个儿子一个儿子的走,而是先确定一个大方向然后跳到下一个分治中心

#include<bits/stdc++.h>
#define N 200050
using namespace std;
typedef long long ll;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
int n, m;
int first[N], nxt[N], to[N], w[N], tot;
void add(int x, int y,int z){
	nxt[++tot] = first[x], first[x] = tot;
	to[tot] = y; w[tot] = z;
}
ll dis[N], st[N][22]; 
int lg[N], sign, id[N];
int Siz, siz[N], Maxson[N], rt; 
void dfs(int u, int f){
	st[++sign][0] = dis[u]; id[u] = sign;
	for(int i=first[u];i;i=nxt[i]){
		int t = to[i]; if(t == f) continue;
		dis[t] = dis[u] + (ll)w[i];  dfs(t, u);
		st[++sign][0] = dis[u];
	}
}
int vis[N];
ll getdis(int u, int v){
	int x = id[u], y = id[v];
	if(x > y) swap(x, y);
	int t = lg[y-x+1];
	return dis[u] + dis[v] - 2 * min(st[x][t], st[y-(1<<t)+1][t]);
}
void getrt(int u, int f){
	siz[u] = 1; Maxson[u] = 0;
	for(int i=first[u];i;i=nxt[i]){
		int t = to[i]; if(t == f || vis[t]) continue;
		getrt(t, u); siz[u] += siz[t];
		Maxson[u] = max(Maxson[u], siz[t]);
	} Maxson[u] = max(Maxson[u], Siz - siz[u]);
	if(Maxson[u] < Maxson[rt]) rt = u;
}
int fa[N]; ll sum[N], dis1[N], dis2[N];
vector<pair<int, int> >son[N];
#define mp make_pair 
void Divide(int u, int f){
	fa[u] = f; vis[u] = 1; int sum = Siz;
	for(int i=first[u];i;i=nxt[i]){
		int t = to[i]; if(vis[t]) continue;
		if(siz[t] > siz[u]) Siz = sum - siz[u];
		else Siz = siz[t];
		rt = 0; getrt(t, 0); son[u].push_back(mp(t, rt)); 
		Divide(rt, u);
	}
}
void update(int u, ll k){
	sum[u] += k;
	for(int i=u; fa[i]; i = fa[i]){
		ll d = getdis(fa[i], u);
		dis2[i] += k * d; dis1[fa[i]] += k * d;
		sum[fa[i]] += k;
	}
}
ll calc(int u){
	ll res = dis1[u];
	for(int i=u; fa[i]; i = fa[i]){
		ll d = getdis(u, fa[i]);
		res += dis1[fa[i]] - dis2[i];
		res += d * (sum[fa[i]] - sum[i]);
	} return res;
}
ll query(int u){
	ll ans = calc(u);
	for(int i=0; i<son[u].size(); i++){
		int t = son[u][i].first, v = son[u][i].second;
		ll tmp = calc(t); if(tmp < ans) return query(v);
	} return ans;
}
int main(){
	n = read(), m = read();
	for(int i=1; i<n; i++){
		int x = read(), y = read(), z = read();
		add(x, y, z); add(y, x, z);
	} dfs(1, 0);
	for(int i=2; i<=sign; i++) lg[i] = lg[i>>1] + 1;
	for(int i=1; (1<<i) <= sign; i++)
		for(int j=1; j+(1<<i)-1<=sign; j++)
			st[j][i] = min(st[j][i-1], st[j+(1<<(i-1))][i-1]);
	Maxson[0] = 1e9; Siz = n; getrt(1, 0); int pre = rt;
	Divide(rt, 0); rt = pre;
	while(m--){
		int u = read(), k = read();
		update(u, k); 
		printf("%lld\n", query(rt));
	} return 0;
}

[Ynoi2011]D1T3
每次找到点分树上最浅的点并且满足它到 x 的路径上的所有点的颜色都在 [ l , r ] [l,r] [l,r] 之间
于是问题转换为每次询问一个点,它的子树中有多少个点到它路径上的颜色在 [ l , r ] [l,r] [l,r] 之间
我们可以求出路径上颜色的 m i n , m a x min,max min,max,就转换为了 l ≤ m i n , m a x ≤ r l\le min,max\le r lmin,maxr
即有多少个颜色存在于一个平面的右上角
对于所有询问 [ l , r ] [l,r] [l,r] 按 l 从大到小排序,扫一遍,同种颜色保留最上面的点树状数组查一下即可

#include<bits/stdc++.h>
#define N 100050
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = (cnt << 1) + (cnt << 3) + (ch-'0'), ch = getchar();
	return cnt * f;
}
int n, m, c[N], exi[N];
int first[N], nxt[N<<1], to[N<<1], tot;
void add(int x, int y){
	nxt[++tot] = first[x], first[x] = tot, to[tot] = y;
}
struct node{ 
	int id, l, r, op;
	node(int a=0, int b=0, int c=0, int d=0){ id = a; l = b; r = c; op = d;}
	bool operator < (const node &a) const{ return r < a.r || (r == a.r && op < a.op);	}
} q[N]; int res, ans[N];
int siz[N], mxson[N], rt, Siz, vis[N];
void getrt(int u, int fa){
	siz[u] = 1; mxson[u] = 0;
	for(int i = first[u]; i; i = nxt[i]){
		int t = to[i]; if(t == fa || vis[t]) continue;
		getrt(t, u); siz[u] += siz[t];
		mxson[u] = max(mxson[u], siz[t]);
	} mxson[u] = max(mxson[u], Siz - siz[u]);
	if(mxson[u] < mxson[rt]) rt = u;
} 
vector<node> v[N];
void dfs(int rot, int u, int fa, int mi, int mx){
	mi = min(mi, u); mx = max(mx, u);
	v[rot].push_back(node(c[u], mi, mx, 0));
	for(int i = first[u]; i; i = nxt[i]){
		int t = to[i]; if(t == fa || vis[t]) continue;
		dfs(rot, t, u, mi, mx);
	}
}
#define pa pair<int,int>
#define mp make_pair
vector<pa > dat[N];
vector<int> fa[N];
void dfs1(int u, int f, int mi, int mx, int rot){
	mi = min(mi, u); mx = max(mx, u); 
	dat[u].push_back(mp(mi, mx));
//	cout<<u<<" "<<rot<<" "<<mi<<" "<<mx<<endl;
	fa[u].push_back(rot);
	for(int i = first[u]; i; i = nxt[i]){
		int t = to[i]; if(t == f || vis[t]) continue;
		dfs1(t, u, mi, mx, rot);
	}
}
void Solve(int u){
	vis[u] = 1; dfs(u, u, 0, u, u);
	int sz = Siz;
	for(int i = first[u]; i; i = nxt[i]){
		int t = to[i]; if(vis[t]) continue;
		if(siz[t] > siz[u]) Siz = sz - siz[u]; else Siz = siz[t];
		rt = 0; getrt(t, u); dfs1(t, 0, u, u, u); 
		Solve(rt);
	} 
}
struct BIT{
	int c[N];
	void Add(int x, int v){ for(;x;x-=x&-x) c[x] += v;}
	int Ask(int x){ int ans = 0; for(;x<=n;x+=x&-x) ans += c[x]; return ans;}
}Bit;
int main(){
	n = read(), m = read();
	for(int i = 1; i <= n; i++) c[i] = read();
	for(int i = 1; i < n; i++){
		int x = read(), y = read();
		add(x, y); add(y, x);
	} rt = 0; mxson[0] = n+1; Siz = n; getrt(1, 0); 
	Solve(rt);
	for(int i = 1; i <= m; i++){
		int l = read(), r = read(), x = read();
		for(int j = 0; j < fa[x].size(); j++){
			if(dat[x][j].first >= l && dat[x][j].second <= r){ x = fa[x][j];}
		} v[x].push_back(node(i, l, r, 1));
	}
	for(int x = 1; x <= n; x++){
		sort(v[x].begin(), v[x].end());
		for(int i = 0; i < v[x].size(); i++){
			if(!v[x][i].op){
				if(exi[v[x][i].id] < v[x][i].l){
					if(exi[v[x][i].id]) Bit.Add(exi[v[x][i].id], -1);
					Bit.Add(v[x][i].l, 1);
					exi[v[x][i].id] = v[x][i].l;
				}
			}
			else ans[v[x][i].id] = Bit.Ask(v[x][i].l);
		} 
		for(int i = 0; i < v[x].size(); i++){
			if(!v[x][i].op){
				if(exi[v[x][i].id]) Bit.Add(exi[v[x][i].id], -1), exi[v[x][i].id] = 0;
			}
		} v[x].clear();
	} 
	for(int i = 1; i <= m; i++) cout << ans[i] << "\n";
	return 0;
}

[Ynoi2011]D2T3
可以按度数分块,对于 > n >\sqrt n >n 的不会超过 n \sqrt n n 个点,用一个数据结构维护
对于 ≤ n \le \sqrt n n 的暴力找
考虑链分治,对于每个点,只维护轻儿子,每次询问先暴力把自己,父亲,重儿子插入,然后查
修改的时候套路一般地改一下重链头父亲的信息就可以了
看一看复杂度,修改 l o g 2 n log^2n log2n, 查询 l o g n ∗ 6 logn*6 logn6 的大常数
其实询问的时候只需要查一下 k , k − 1 , k − 2 , k − 3 k,k-1,k-2,k-3 k,k1,k2,k3,然后跟自己父亲重儿子比一下就可以了
只有 4 倍小常数就卡过了

#include<bits/stdc++.h>
#define N 1200050
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = (cnt << 1) + (cnt << 3) + (ch-'0'), ch = getchar();
	return cnt * f;
}
int n, m;
int first[N], nxt[N<<1], to[N<<1], tot;
void add(int x, int y){ nxt[++tot] = first[x], first[x] = tot, to[tot] = y;}
int a[N];
int c[N];
void Add(int x, int v){ for(;x<=n;x+=x&-x) c[x] += v;}
int Ask(int x){ int ans = 0; for(;x;x-=x&-x) ans += c[x]; return ans;}
int dep[N], fa[N], top[N], siz[N], son[N], id[N], sign;

int rnd(){ return (rand() << 15) | rand();}
struct Treap{
	int ch[2], siz, val, rnd, cnt;
}t[N];
#define ls t[x].ch[0]
#define rs t[x].ch[1]
int rt[N], node;
stack<int> bin;
int newnode(int v){ 
	int now = 0;
	if(bin.size()) now = bin.top(), bin.pop();
	else now = ++node; 
	t[now].ch[0] = t[now].ch[1] = 0; t[now].siz = t[now].cnt = 1; 
	t[now].val = v; t[now].rnd = rnd();
	return now;
}
void pushup(int x){ t[x].siz = t[ls].siz + t[rs].siz + t[x].cnt;}
void Lrot(int &x){ int y = ls; ls = t[y].ch[1]; t[y].ch[1] = x; t[y].siz = t[x].siz; pushup(x); x = y;}
void Rrot(int &x){ int y = rs; rs = t[y].ch[0]; t[y].ch[0] = x; t[y].siz = t[x].siz; pushup(x); x = y;}
void Insert(int &x, int v){
	if(!x){ x = newnode(v); return;} t[x].siz++;
	if(v == t[x].val){ t[x].cnt++; return;}
	if(v < t[x].val){ Insert(ls, v); if(t[ls].rnd < t[x].rnd) Lrot(x);}
	if(v > t[x].val){ Insert(rs, v); if(t[rs].rnd < t[x].rnd) Rrot(x);}
}
void Delete(int &x, int v){
	if(!x) return;
	if(v == t[x].val){ 
		if(t[x].cnt > 1){ t[x].cnt--; t[x].siz--; return;}
		if(!ls || !rs) bin.push(x), x = ls + rs;
		else{ if(t[ls].rnd < t[rs].rnd) Lrot(x); else Rrot(x); Delete(x, v);}
		return;
	} t[x].siz--; if(v < t[x].val) Delete(ls, v); 
	else Delete(rs, v);
}
int Rank(int x, int k){
	if(t[ls].siz >= k) return Rank(ls, k);
	if(t[ls].siz + t[x].cnt < k) return Rank(rs, k - t[ls].siz - t[x].cnt);
	return t[x].val;
}
void dfs1(int u, int f){
	siz[u] = 1;
	for(int i = first[u]; i; i = nxt[i]){
		int t = to[i]; if(t == f) continue;
		fa[t] = u; dep[t] = dep[u] + 1; dfs1(t, u);
		siz[u] += siz[t]; if(siz[t] > siz[son[u]]) son[u] = t;
	}
}
void dfs2(int u, int Top){
	top[u] = Top; id[u] = ++sign; Add(sign, a[u]); Add(sign+1, -a[u]);
	if(son[u]) dfs2(son[u], Top);
	for(int i = first[u]; i; i = nxt[i]){
		int t = to[i]; if(t == fa[u] || t == son[u]) continue;
		Insert(rt[u], a[t]); dfs2(t, t); 
	} 
}
void change(int x, int v){
	if(!fa[x]) return;
	int pre = Ask(id[x]);
	Delete(rt[fa[x]], pre);
	Insert(rt[fa[x]], pre + v);
}
void modify(int x, int y, int v){
	while(top[x] != top[y]){
		if(dep[top[x]] < dep[top[y]]) swap(x, y);
		change(top[x], v); 
		Add(id[top[x]], v); Add(id[x]+1, -v);
		x = fa[top[x]];
	}
	if(id[x] > id[y]) swap(x, y); 
	if(top[x] == x)	change(x, v);
	Add(id[x], v); Add(id[y]+1, -v);
}
int tmp[10];
int main(){
	srand(time(0)); srand(rand());
	n = read(), m = read();
	for(int i = 1; i <= n; i++) a[i] = read();
	for(int i = 1; i < n; i++){
		int x = read(), y = read();
		add(x, y); add(y, x);
	} dep[1] = 1; dfs1(1, 0); dfs2(1, 1);
	while(m--){
		int op = read(), x = read(), y = read();
		if(op == 1){ int v = read(); modify(x, y, v);}
		if(op == 2){
			int ret = 0;
			if(y <= t[rt[x]].siz)tmp[++ret] = Rank(rt[x], y);
			if(y > 1 && y-1 <= t[rt[x]].siz) tmp[++ret] = Rank(rt[x], y-1);
			if(y > 2 && y-2 <= t[rt[x]].siz) tmp[++ret] = Rank(rt[x], y-2);
			if(y > 3 && y-3 <= t[rt[x]].siz) tmp[++ret] = Rank(rt[x], y-3);
			tmp[++ret] = Ask(id[x]);
			if(fa[x]) tmp[++ret] = Ask(id[fa[x]]);
			if(son[x]) tmp[++ret] = Ask(id[son[x]]);
			sort(tmp+1, tmp+ret+1);
			if(y <= 3) cout << tmp[y] << "\n";
			else cout << tmp[4] << "\n";
		}
	} 
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值