题目大意:给出一颗无根树,有链的修改操作,还有子树的查询。除此之外,还有选定这棵树的一个点为根。
思路:子树操作,链上修改,带size域的树链剖分就可以搞定。换根肯定不能真的换,出题人要是闲的没事所有操作都在换根就惨。我们可以画一张图模拟下换根。先按照读入的顺序建一颗有根树,然后观察当前的根在要询问的点的位置。如果当前的根在要询问的点的儿子中,那么那个点为根的时候,当前点的子树就是除了当前点的有根节点的子树的点意外的所有点。如果根不在当前点的子树中,那么当前点的子树不变。还有一种情况要特殊讨论一下,当前点就是根,那么子树就是整棵树。
PS:一个点的子树的范围是pos[x]~pos[x] + size[x] - 1
CODE:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 100010
#define INF 0x3f3f3f3f
#define LEFT (pos << 1)
#define RIGHT (pos << 1|1)
using namespace std;
struct SegmentTree{
int _min;
bool v;
}tree[MAX << 2];
int points,asks;
int head[MAX],total;
int next[MAX << 1],aim[MAX << 1];
int deep[MAX],father[MAX],size[MAX],son[MAX];
int pos[MAX],top[MAX],cnt;
int capital;
inline void Add(int x,int y)
{
next[++total] = head[x];
aim[total] = y;
head[x] = total;
}
void PreDFS(int x,int last)
{
deep[x] = deep[last] + 1;
father[x] = last;
size[x] = 1;
for(int i = head[x]; i; i = next[i]) {
if(aim[i] == last) continue;
PreDFS(aim[i],x);
size[x] += size[aim[i]];
if(size[aim[i]] > size[son[x]])
son[x] = aim[i];
}
}
void DFS(int x,int last,int _top)
{
pos[x] = ++cnt;
top[x] = _top;
if(son[x]) DFS(son[x],x,_top);
for(int i = head[x]; i; i = next[i]) {
if(aim[i] == last || aim[i] == son[x]) continue;
DFS(aim[i],x,aim[i]);
}
}
inline bool InSonTree(int x,int f)
{
if(pos[x] < pos[f] || pos[x] > pos[f] + size[f] - 1) return false;
return true;
}
inline void PushDown(int pos)
{
if(tree[pos].v) {
tree[LEFT]._min = tree[pos]._min;
tree[RIGHT]._min = tree[pos]._min;
tree[LEFT].v = tree[RIGHT].v = true;
tree[pos].v = false;
}
}
void Modify(int l,int r,int x,int y,int pos,int c)
{
if(l == x && y == r) {
tree[pos]._min = c;
tree[pos].v = true;
return ;
}
PushDown(pos);
int mid = (l + r) >> 1;
if(y <= mid) Modify(l,mid,x,y,LEFT,c);
else if(x > mid) Modify(mid + 1,r,x,y,RIGHT,c);
else {
Modify(l,mid,x,mid,LEFT,c);
Modify(mid + 1,r,mid + 1,y,RIGHT,c);
}
tree[pos]._min = min(tree[LEFT]._min,tree[RIGHT]._min);
}
inline void Modify(int x,int y,int c)
{
while(top[x] != top[y]) {
if(deep[top[x]] < deep[top[y]])
swap(x,y);
Modify(1,cnt,pos[top[x]],pos[x],1,c);
x = father[top[x]];
}
if(deep[x] < deep[y]) swap(x,y);
Modify(1,cnt,pos[y],pos[x],1,c);
}
int Ask(int l,int r,int x,int y,int pos)
{
if(l == x && y == r) return tree[pos]._min;
PushDown(pos);
int mid = (l + r) >> 1;
if(y <= mid) return Ask(l,mid,x,y,LEFT);
if(x > mid) return Ask(mid + 1,r,x,y,RIGHT);
int left = Ask(l,mid,x,mid,LEFT);
int right = Ask(mid + 1,r,mid + 1,y,RIGHT);
return min(left,right);
}
int main()
{
cin >> points >> asks;
for(int x,y,i = 1; i < points; ++i) {
scanf("%d%d",&x,&y);
Add(x,y),Add(y,x);
}
PreDFS(1,0);
DFS(1,0,1);
for(int x,i = 1; i <= cnt; ++i) {
scanf("%d",&x);
Modify(1,cnt,pos[i],pos[i],1,x);
}
cin >> capital;
for(int flag,i = 1; i <= asks; ++i) {
scanf("%d",&flag);
int x,y,z;
if(flag == 1) scanf("%d",&capital);
if(flag == 2) {
scanf("%d%d%d",&x,&y,&z);
Modify(x,y,z);
}
if(flag == 3) {
scanf("%d",&x);
if(x == capital)
printf("%d\n",tree[1]._min);
else {
int son = 0;
for(int j = head[x]; j && !son; j = next[j]) {
if(aim[j] == father[x]) continue;
if(InSonTree(capital,aim[j]))
son = aim[j];
}
if(son) {
int left = Ask(1,cnt,1,pos[son] - 1,1);
int right = INF;
if(pos[son] + size[son] <= cnt)
right = Ask(1,cnt,pos[son] + size[son],cnt,1);
printf("%d\n",min(min(left,right),Ask(1,cnt,pos[x],pos[x],1)));
}
else printf("%d\n",Ask(1,cnt,pos[x],pos[x] + size[x] - 1,1));
}
}
}
return 0;
}