bzoj 3083 树链剖分
首先我们先将树提出一个根变成有根树,那么我们可以通过树链剖分来实现对于子树的最小值求解,那么按照当前的根和询问的点的相对位置关系我们可以将询问变成某个子树和或者除去某颗子树之后其余的和,前者直接询问区间,后者询问区间的补集。
/************************************************************** 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; }