bzoj3306: 树
Description
给定一棵大小为 n 的有根点权树,支持以下操作:
• 换根
• 修改点权
• 查询子树最小值
Input
第一行两个整数 n, Q ,分别表示树的大小和操作数。
接下来n行,每行两个整数f,v,第i+1行的两个数表示点i的父亲和点i的权。保证f < i。如 果f = 0,那么i为根。输入数据保证只有i = 1时,f = 0。
接下来 m 行,为以下格式中的一种:
• V x y表示把点x的权改为y
• E x 表示把有根树的根改为点 x
• Q x 表示查询点 x 的子树最小值
Output
对于每个 Q ,输出子树最小值。
Sample Input
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
Sample Output
1
2
3
4
HINT
对于 100% 的数据:n, Q ≤ 10^5。
分析
换根搞子树有个神仙操作,先转有根树,然后考虑换的根u和子树根v关系。如果
不会有任何影响,否则就是挖掉子树根在根方向上的儿子的子树的所有节点。用倍增+线段树搞就可以了。
然而,会LCT就是不讲道理的。
LCT+set+维护子树信息。
妥妥的。
跑得慢?你需要一个三级高速缓存优化吗?
代码
#include<cstdio>
#include<set>
#include<algorithm>
const int N = 1e5 + 10; using std::min;
int ri() {
char ch = getchar(); int x = 0; for(;ch < '0' || ch > '9'; ch = getchar()) ;
for(;ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) - '0' + ch; return x;
}
int rc() {
char ch = getchar(); for(;ch != 'V' && ch != 'Q' && ch != 'E'; ch = getchar()) ;
return ch == 'V' ? 1 : (ch == 'E' ? 2 : 3);
}
std::multiset<int>s[N]; int r[N], fa[N], ch[N][2], mn[N], v[N], st[N], tp;
void Era(int u, int v) {s[u].erase(s[u].find(v));}
bool wh(int p) {return ch[fa[p]][1] == p;}
bool Ir(int p) {return ch[fa[p]][0] != p && ch[fa[p]][1] != p;}
void Down(int p) {
if(r[p]) std::swap(ch[p][0], ch[p][1]), r[ch[p][0]] ^= 1, r[ch[p][1]] ^= 1, r[p] = 0;
}
void Up(int p) {mn[p] = min(min(mn[ch[p][0]], mn[ch[p][1]]), *(s[p].begin()));}
void Rotate(int p) {
int f = fa[p], g = fa[f], c = wh(p);
if(!Ir(f)) ch[g][wh(f)] = p; fa[p] = g;
ch[f][c] = ch[p][c ^ 1]; if(ch[f][c]) fa[ch[f][c]] = f;
ch[p][c ^ 1] = f; fa[f] = p; Up(f);
}
void Splay(int p) {
st[tp = 1] = p; for(int i = p; !Ir(i); i = fa[i]) st[++tp] = fa[i];
for(int i = tp; i; --i) Down(st[i]);
for(;!Ir(p); Rotate(p)) if(!Ir(fa[p])) Rotate(wh(fa[p]) == wh(p) ? fa[p] : p);
Up(p);
}
void Access(int p) {
for(int pr = 0; p; p = fa[pr = p]) {
Splay(p); if(pr) Era(p, mn[pr]);
if(ch[p][1]) s[p].insert(mn[ch[p][1]]);
ch[p][1] = pr; Up(p);
}
}
void Get(int u) {Access(u); Splay(u);}
int main() {
int n = ri(), m = ri(); mn[0] = 1e9;
for(int i = 1;i <= n; ++i) fa[i] = ri(), s[i].insert(v[i] = ri());
for(int i = n; i; --i) Up(i), s[fa[i]].insert(mn[i]);
for(;m--;) {
int op = rc(), x = ri();
if(op == 1) Get(x), Era(x, v[x]), s[x].insert(v[x] = ri()), Up(x);
if(op == 2) Get(x), r[x] ^= 1;
if(op == 3) Get(x), printf("%d\n", *(s[x].begin()));
}
return 0;
}