「BZOJ3306」树
题目
一棵有根点权树有 n 个结点,编号为 1 的结点是根节点,第 i 个结点的父亲结点为\(f_i\),权值为\(a_i\)。
接着进行 m 次以下操作:
- V x y 表示将点 x 的权修改为 y;
- E x 表示把有根树的根改为点 x;
- Q x 表示查询点 x 的子树最小值。
Input Format
第一行两个整数 n,m。
接下来 n 行,每行两个整数\(f_i,v_i\)
接下来 m 行,每行一次操作。
Output Format
对于每次 Q 操作,输出子树最小值。
样例
算了样例太水了没啥参考性
输入:
7 6
0 4
1 9
1 9
2 8
3 6
2 1
1 9
E 5
Q 5
Q 1
V 3 6
V 2 1
E 6
输出:
1
1
解题思路
其实如果没有换根,这就是一个线段树维护的子树最大值。dfn序。
如果加上了换根,我们就要去考虑当前询问的节点 x 和root有什么关系,首先肯定是在原树上进行操作。
- Obviously,如果x==root,那么就直接query整棵树。这个先判断。
- Furthermore,如果当前的lca(x,root)!= x,那么x的答案与root无关,直接查询 x 即可。
- Last but not least,如果 lca(x,root)== x,那么查询的子树是除了x->root这一颗子树之外的所有。
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define ls rt<<1
#define rs rt<<1|1
using namespace std;
const int N=1e5+10;
int n,cnt,h[N],ans,root=1,size[N],cow,dfn[N],f[N][25],val[N],dep[N],b[N],step,m;
struct st{
int e,ne;
}a[N<<1];
struct tree{
int k,mn;
}tr[N<<2];
void add(int x,int y){
a[++cnt].e=y;
a[cnt].ne=h[x];
h[x]=cnt;
}
void dfs(int k,int fa){
dfn[k]=++step;
size[k]=1;
f[k][0]=fa;
dep[k]=dep[fa]+1;
for(int i=1;i<=20;i++) f[k][i]=f[f[k][i-1]][i-1];
b[step]=val[k];
for(int i=h[k];i;i=a[i].ne){
if(a[i].e==fa) continue;
dfs(a[i].e,k);
size[k]+=size[a[i].e];
}
}
void pushup(int rt){tr[rt].mn=min(tr[ls].mn,tr[rs].mn);}
void build(int rt,int l,int r){
if(l==r){tr[rt]={b[l],b[l]};return ;}
int mid=l+r>>1;build(ls,l,mid);build(rs,mid+1,r);pushup(rt);
}
void change(int rt,int l,int r,int k,int v){
if(l==r){tr[rt]={v,v};return ;}
int mid=l+r>>1;
if(k<=mid) change(ls,l,mid,k,v);
else change(rs,mid+1,r,k,v);
pushup(rt);
}
int query(int rt,int l,int r,int x,int y){
if(x>y || !x || !y) return 1e9;
if(l>=x && r<=y) return tr[rt].mn;
int mid=l+r>>1,s=1e9;
if(x<=mid) s=min(s,query(ls,l,mid,x,y));
if(y>mid) s=min(s,query(rs,mid+1,r,x,y));
return s;
}
int lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=20;i>=0;i--){
if(dep[f[x][i]]>=dep[y]) x=f[x][i];
}
if(x==y) return x;
for(int i=20;i>=0;i--){
if(f[x][i]!=f[y][i]){
x=f[x][i];y=f[y][i];
}
}
return f[x][0];
}
signed main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++){
int x,y;
scanf("%lld%lld",&x,&val[i]);
add(x,i);
add(i,x);
}
dfs(1,0);
build(1,1,n);
while(m--){
char op;
cin>>op;
if(op=='V'){
int x,y;
scanf("%lld%lld",&x,&y);
change(1,1,n,dfn[x],y);
}
if(op=='Q'){
int x;
scanf("%lld",&x);
int fa=lca(x,root);
if(x==root){
printf("%lld\n",query(1,1,n,1,n));
continue;
}
if(fa!=x){
printf("%lld\n",query(1,1,n,dfn[x],dfn[x]+size[x]-1));
}
else{
int p=root;
if(dep[p]<dep[x]) swap(p,x);
for(int i=20;i>=0;i--){
if(dep[f[p][i]]>dep[x]){
p=f[p][i];
}
if(f[p][0]==x) break;
}
int o1=query(1,1,n,dfn[p]+size[p],n);
int o2=query(1,1,n,dfn[1],dfn[p]-1);
printf("%lld\n",min(o1,o2));
}
}
if(op=='E') scanf("%lld",&root);
}
}
碎碎念:
数组一定不要卡死开我真服了。
区间最值,查询之类的问题多想想线段树!!!
树上问题多想lca。
(最终解释权归16:36的脑子所有)
posted on 2026-09-30 16:40 FAYEEEEEEE 阅读(5) 评论(0) 收藏 举报
浙公网安备 33010602011771号