首先我们先将树提出一个根变成有根树,那么我们可以通过树链剖分来实现对于子树的最小值求解,那么按照当前的根和询问的点的相对位置关系我们可以将询问变成某个子树和或者除去某颗子树之后其余的和,前者直接询问区间,后者询问区间的补集。
/**************************************************************
Problem: 3083
User: BLADEVIL
Language: C++
Result: Accepted
Time:6412 ms
Memory:43904 kb
****************************************************************/#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 200010
#define maxm 400010
#define inf (~0U>>1)using namespace std;
struct segment_tree {
int left,right,min_,lazy;
}t[maxn<<2];int n,m,l,rot,tot;
int key[maxn];
int other[maxm],pre[maxm],last[maxn];
int size[maxn],max_son[maxn],top[maxn],dep[maxn],a[maxn],num[maxn][2],jump[maxn][20],dis[maxn];void connect(int x,int y) {
pre[++l]=last[x];
last[x]=l;
other[l]=y;
}void update(int x) {
t[x].min_=min(t[x<<1].min_,t[(x<<1)+1].min_);
}void push_down(int x) {
t[x<<1].lazy=t[(x<<1)+1].lazy=t[x].lazy;
t[x<<1].min_=t[(x<<1)+1].min_=t[x].min_;
t[x].lazy=0;
}int get_lca(int x,int y) {
if (dis[x]>dis[y]) swap(x,y);
int det=dis[y]-dis[x];
for (int j=0;j<=18;j++) if (det&(1<<j)) y=jump[y][j];
if (x==y) return x;
for (int j=18;j>=0;j--)
if (jump[x][j]!=jump[y][j]) x=jump[x][j],y=jump[y][j];
return jump[x][1];
}int get_lca_son(int x,int y) {
if (dis[x]>dis[y]) swap(x,y);
for (int j=18;j>=0;j--)
if (dis[jump[y][j]]>dis[x]) y=jump[y][j];
return y;
}void dfs(int x) {
size[x]=1;
for (int p=last[x];p;p=pre[p]) {
if (dis[other[p]]) continue;
jump[other[p]][0]=x; dis[other[p]]=dis[x]+1;
dfs(other[p]);
size[x]+=size[other[p]];
if (size[other[p]]>size[max_son[x]]) max_son[x]=other[p];
}
}void make_segment(int x) {
num[x][0]=++tot; a[tot]=key[x];
if (max_son[x]) {
top[max_son[x]]=top[x];
dep[max_son[x]]=dep[x];
make_segment(max_son[x]);
}
for (int p=last[x];p;p=pre[p]) {
if (other[p]==jump[x][0]) continue;
if (other[p]==max_son[x]) continue;
top[other[p]]=other[p];
dep[other[p]]=dep[x]+1;
make_segment(other[p]);
}
num[x][1]=tot;
}void build_segment(int x,int l,int r) {
t[x].left=l; t[x].right=r;
if (t[x].left==t[x].right) {
t[x].min_=a[t[x].left];
return ;
}
int mid=t[x].left+t[x].right>>1;
build_segment(x<<1,l,mid); build_segment((x<<1)+1,mid+1,r);
update(x);
}void change_segment_tree(int x,int l,int r,int y) {
if (t[x].lazy) push_down(x);
if ((t[x].left==l)&&(t[x].right==r)) {
t[x].lazy=t[x].min_=y;
return ;
}
int mid=t[x].left+t[x].right>>1;
if (l>mid) change_segment_tree((x<<1)+1,l,r,y); else
if (r<=mid) change_segment_tree(x<<1,l,r,y); else
change_segment_tree(x<<1,l,mid,y),change_segment_tree((x<<1)+1,mid+1,r,y);
update(x);
}void change(int x,int y,int z) {
if (dep[x]>dep[y]) swap(x,y);
while (dep[x]!=dep[y]) {
change_segment_tree(1,num[top[y]][0],num[y][0],z);
y=jump[top[y]][0];
}
while (top[x]!=top[y]) {
change_segment_tree(1,num[top[x]][0],num[x][0],z);
change_segment_tree(1,num[top[y]][0],num[y][0],z);
x=jump[top[x]][0];
y=jump[top[y]][0];
}
if (num[x][0]>num[y][0]) swap(x,y);
change_segment_tree(1,num[x][0],num[y][0],z);
}int query_segment_tree(int x,int l,int r) {
if (l>r) return inf;
if (t[x].lazy) push_down(x);
if ((t[x].left==l)&&(t[x].right==r)) return t[x].min_;
int mid=t[x].left+t[x].right>>1;
if (l>mid) return query_segment_tree((x<<1)+1,l,r); else
if (r<=mid) return query_segment_tree(x<<1,l,r); else
return min(query_segment_tree(x<<1,l,mid),query_segment_tree((x<<1)+1,mid+1,r));
}void query(int x) {
if (x==rot) {
printf("%d\n",t[1].min_);
return ;
}
if (get_lca(x,rot)!=x) printf("%d\n",query_segment_tree(1,num[x][0],num[x][1])); else {
int y=get_lca_son(x,rot);
printf("%d\n",min(query_segment_tree(1,1,num[y][0]-1),query_segment_tree(1,num[y][1]+1,n)));
//printf("%d %d\n",query_segment_tree(1,1,num[y][0]-1),query_segment_tree(1,num[y][1]+1,n));
//printf("%d\n",y);
//printf("%d %d\n",num[y][0],num[y][1]);
}
}int main() {
//freopen("country.in","r",stdin); freopen("country.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<n;i++) {
int x,y; scanf("%d%d",&x,&y);
connect(x,y); connect(y,x);
}
for (int i=1;i<=n;i++) scanf("%d",&key[i]);
scanf("%d",&rot);
dis[rot]=1;
dfs(rot);
top[rot]=rot; dep[rot]=1;
make_segment(rot);
//for (int i=1;i<=n;i++) printf("%d %d %d %d %d\n",i,max_son[i],size[i],num[i][0],num[i][1]);
build_segment(1,1,n);
for (int j=1;j<=18;j++)
for (int i=1;i<=n;i++) jump[i][j]=jump[jump[i][j-1]][j-1];
while (m--) {
int opt; scanf("%d",&opt);
if (opt==1) scanf("%d",&rot); else
if (opt==2) {
int l,r,y; scanf("%d%d%d",&l,&r,&y);
change(l,r,y);
} else {
int x; scanf("%d",&x);
query(x);
}
}
return 0;
}
bzoj 3083 树链剖分