BZOJ3306树

好像是2010国家集训队的题来着

具体都写代码里了

真的是好题

就是记得宏定义的时候把括号加好,优先级真的是一个很坑人的问题, 我花了一整个上午来吸取这个教训

 

点击查看代码


#include <bits/stdc++.h>
#define Re register int 
#define LL long long
#define ki kiritokazuto
using namespace std;
static const int maxn = 1e6 + 10000;

inline void in(int &x) {
	int f = 0; x = 0; char c = getchar();
	while(c < '0' || c > '9') f |= c == '-', c = getchar();
	while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
	x = f ? -x : x;	
}
int root, str[maxn], endd[maxn], depth[maxn];
int tim = 0;
int n, Q;
int seq[maxn];
int val[maxn];
int fa[maxn][22];//倍增向上翻爹fa 
inline void ot(int x) {
	if(x < 0)putchar('-'), x = -x;
	if(x > 9)ot(x / 10); putchar(x % 10 + '0');
}
	 
struct Shu {
	int to, next;
}a[maxn << 2];
int len, head[maxn];
int t = 0;
struct Node {
	 int l, r;
	 int Min;
	 #define lsp rt << 1
	 #define rsp rt << 1 | 1
	 #define mid ((l + r) >> 1)
//宏定义害的我好惨啊!!! 调了一上午,不用宏就对了??? 
}tr[maxn << 2];

void Qian(int from, int to) {
	a[++len].to = to;
	a[len].next = head[from];
	head[from] = len;
}

void dfs(int x) {
	str[x] = ++tim;
	seq[tim] = x;//序列sequence
	for(int i = 1; i <= t; i ++) {
		if(depth[x] < (1 << i))break;//x的深度不支持x向上翻找爹(简称《深 度 不 够》) 
		 fa[x][i] = fa[fa[x][i - 1]][i - 1];//倍增 
	}
	for(int i = head[x], to; i; i = a[i].next) {
		to = a[i].to;
		depth[to] = depth[x] + 1;
		fa[to][0] = x;//儿子的爹是爹,f数组里不存自己(简称《带 孝 子》) 
		dfs(to);
	}
	endd[x] = tim;
}

inline void pushup(int rt) {
	tr[rt].Min = min(tr[lsp].Min, tr[rsp].Min);
//	Min(rt) = min(Min(lsp), Min(rsp));
}
 
void build(int rt, int l, int r) {
	tr[rt].l = l;
	tr[rt].r = r;
//	  int mid = (l + r) >> 1;
	if(l == r) {
		tr[rt].Min = val[seq[l]];
		return; 
	}
	build(lsp, l, mid);
	build(rsp, mid + 1, r);
	pushup(rt);
}

void modify(int rt, int pos, int w) {//当前根,目标点pos,修改为w
	 int l = tr[rt].l;
	 int r = tr[rt].r;
	//  int mid = (l + r) >> 1;
	 while(l == r) {
	 	tr[rt].Min = w;
	 	return; 
	 } 
	 if(pos <= mid) modify(lsp, pos, w);
	 else modify(rsp, pos, w);
	 pushup(rt);
}

int query(int rt, int L, int R) {//老三样QWQ
	int l = tr[rt].l;
	int r = tr[rt].r;
//	 int mid = (l + r) >> 1;
	if(L <= l && r <= R) {
		return tr[rt].Min;
	} 
	int vax = 2147483647;
	if(L <= mid) vax = min(vax, query(lsp, L, R));
	if(R > mid) vax = min(vax, query(rsp, L, R));
	return vax;
}

signed main() {
	in(n);
	in(Q);
	 t = (int)(log(n) / log(2)) + 1; 
	int root = 1;
	for(int i = 1, f, vall; i <= n; i ++) {
		in(f);
		in(vall);
		val[i] = vall;
		if(f)Qian(f, i);//它爹指向它 
	}
	dfs(root);
	build(1, 1, n);
	char s[6];
	for(int i = 1, x, w; i <= Q; i ++) {
		cin >> s;
		in(x);
		if(s[0] == 'V') {
			in(w);
			modify(1, str[x], w);//原树还是以1为根去改,就是查的时候抛去 
		}else if(s[0] == 'E') 
			root = x;
		else {
			if(root == x) {
				cout << tr[1].Min << endl;//根就算它本身直接输出 
			}else if(str[x] <= str[root] && endd[x] >= endd[root]) {
				//即是x为新根的祖先,dfs序里x括着root
				int rt = root;
				int deep = depth[rt] - depth[x] - 1;//根是儿子,根深
				//减一是因为
				//1 2 3 4 5 6 根是6, x是 2
				//6 - 2是4
				//但是祖先只有3 4 5三个,抛去6本身
				//所以多减一个一
				for(int j = 0; j <= t; j ++) {
					if(deep & (1 << j))rt = fa[rt][j];
				} 
				//二进制拆分比如x是第13倍父亲
				//1101
				//从0向上翻1找到1改一下
				printf("%d\n", min(query(1, 1, str[rt] - 1), query(1, endd[rt] + 1, n))); 
			}else {
				printf("%d\n", query(1, str[x], endd[x]));
				//	其余两种情况没影响,直接查 
			}
			
			
			
		}
	}
	//换根之后总共三种情况,查询点在原树的时候是新根的儿子,直接搜没影响 
	//查询点在原树的时候与新根没关系,直接搜没影响 
	//查询点在原树的时候是新根的祖先,找到新根到查询点在原树路径上离新根最远的一个点(除去查询点)
	//把那个点到新根以及新根的子树都删掉后,在树里跑min
	//那么dfs序中的范围就是([1,str[y]-1], [endd[y]+1, n]). 
	return 0; 
} 

/*
3 7
0 1
1 2
1 3
Q 1
V 1 6
Q 1
V 2 5
Q 1
V 3 4
Q 1

*/

posted @ 2022-02-20 15:08  kiritokazuto  阅读(50)  评论(0)    收藏  举报