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
*/
愿你在冷铁卷刃之前,得以窥见天光

浙公网安备 33010602011771号