3306: 树
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1451 Solved: 475
[Submit][Status][Discuss]
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
Source
我们先以1为根dfs并建立倍增数组,然后如果根换成了rt,然后要查询x子树内的最小值。我们分情况讨论:
1)若x==rt,则直接输出整棵树的最小值
2)若lca(x,rt)既不等于x那么直接输出x的子树内的最小值
3)若lca(x,rt)==x那么我们发现整棵树除了x向下走可以到达rt的子树之外全部成了x在rt为根下的子树,那我们把这棵子树中最接近x的节点y求出,在整个区间中踢掉y在1根下子树的范围即可。
#include <bits/stdc++.h>
#define ll long long
#define eps 1e-7
#define inf 100000000
using namespace std;
inline int read(){
int x=0;int f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
return x*f;
}
const int MAXN=1e6+10;
struct node{
int y,next;
}e[MAXN];
struct sigment_tree{
int mn;
}T[MAXN*4];
int linkk[MAXN],len,n,m,in[MAXN],out[MAXN],dfs_clock,dep[MAXN],f[MAXN][25],rt,x,y,val[MAXN],ine[MAXN];
inline void insert(int xx,int yy){
e[++len].y=yy;e[len].next=linkk[xx];linkk[xx]=len;
}
inline void dfs(int st,int fa){
in[st]=++dfs_clock;dep[st]=dep[fa]+1;f[st][0]=fa;ine[dfs_clock]=st;
for(int i=linkk[st];i;i=e[i].next){
dfs(e[i].y,st);
}
out[st]=dfs_clock;
}
void getanser(){
for(int i=1;i<=20;i++){
for(int j=1;j<=n;j++){
if(f[j][i-1]) f[j][i]=f[f[j][i-1]][i-1];
}
}
}
inline void add(int l,int r,int root){
if(l>x||r<x) return;
if(l==r){
T[root].mn=y;return;
}
int mid=(r+l)>>1;
add(l,mid,root<<1);
add(mid+1,r,root<<1|1);
T[root].mn=min(T[root<<1].mn,T[root<<1|1].mn);
}
inline void build(int l,int r,int root){
if(l==r){
T[root].mn=val[ine[l]];
return;
}
int mid=(l+r)>>1;
build(l,mid,root<<1);
build(mid+1,r,root<<1|1);
T[root].mn=min(T[root<<1].mn,T[root<<1|1].mn);
}
inline int query(int l,int r,int root){
if(l>y||r<x) return inf;
int mid=(l+r)>>1;
if(x<=l&&y>=r) return T[root].mn;
return min(query(l,mid,root<<1),query(mid+1,r,root<<1|1));
}
void init(){
n=read();m=read();
for(int i=1;i<=n;i++){
int xx=read();val[i]=read();
if(xx) insert(xx,i);
}
dfs(rt=1,0);
build(1,n,1);
getanser();
}
void solve(){
for(int i=1;i<=m;i++){
char ch[5];
scanf("%s",ch);x=read();
if(ch[0]=='V'){
x=in[x];y=read();
add(1,n,1);
}
else if(ch[0]=='E') rt=x;
else{
if(rt==x) printf("%d\n",T[1].mn);
else{
if(in[x]<=in[rt]&&out[x]>=out[rt]){
int depth=dep[rt]-dep[x]-1;int yy=rt;
for(int i=20;i>=0;i--){
if(depth-(1<<i)>=0) yy=f[yy][i],depth-=(1<<i);
}
x=1;y=in[yy]-1;
int minn=query(1,n,1);
x=out[yy]+1;y=n;
minn=min(minn,query(1,n,1));
printf("%d\n",minn);
}
else{
y=out[x];x=in[x];
printf("%d\n",query(1,n,1));
}
}
}
}
}
int main(){
//freopen("All.in","r",stdin);
//freopen("ba.out","w",stdout);
init();
solve();
return 0;
}
对拍代码
#include <bits/stdc++.h>
using namespace std;
int main(){
srand(time(int(NULL)));
freopen("All.in","w",stdout);
int n=rand()%10007;int m=rand()%10007;
cout<<n<<' '<<m<<endl;
cout<<0<<' '<<rand()%10007<<endl;
for(int i=2;i<=n;i++){
int k=rand()%i;if(!k) k++;
printf("%d %d\n",k,rand()%10007);
}
for(int i=1;i<=m;i++){
int k=rand()%3;
if(k==0){
printf("V %d %d\n",rand()%n+1,rand()%10007);
}
if(k==1) printf("Q %d\n",rand()%n+1);
if(k==2) printf("E %d\n",rand()%n+1);
}
return 0;
}