给定一棵大小为 n 的有根点权树,支持以下操作:
• 换根
• 修改点权
• 查询子树最小值
给定一棵大小为 n 的有根点权树,支持以下操作:
• 换根
• 修改点权
• 查询子树最小值
第一行两个整数 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 的子树最小值
对于每个 Q ,输出子树最小值。
对于 100% 的数据:n, Q ≤ 10^5。
树上单点修改、子树查询,很明显DFS序+线段树。
唯一的问题就是换根操作怎么处理?
其实我们不需要改变树的形状,只需要记录当前根是哪个节点。每次询问时判断根和询问节点x的位置关系。
① 如果x是根,则输出整棵树的最小值。
② 如果x是根的祖先,则找到根的子树中最接近x的节点y,输出整棵树减去y的子树的最小值。
③ 如果x既不是根也不是根的祖先,则输出x的子树的最小值。
一开始RE了很多次,因为query中没有判断l>r(其实现在也不是特别懂)。之后又WA了很多次,因为找节点y的倍增算法写错了。
#include
#include
#include
#include
#include
#include
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define maxn 100005
#define inf 1000000000
using namespace std;
int n,q,rt,x,y,tmp,cnt,tot;
int a[maxn],b[maxn],d[maxn],l[maxn],r[maxn],head[maxn],f[maxn][21];
struct edge_type
{
int next,to;
}e[maxn];
struct seg
{
int l,r,mn;
}t[maxn*4];
inline int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline void add_edge(int x,int y)
{
e[++cnt]=(edge_type){head[x],y};head[x]=cnt;
}
inline void dfs(int x)
{
b[++tot]=x;l[x]=tot;
F(i,1,20)
{
if (1<>1;
build(k<<1,l,mid);build(k<<1|1,mid+1,r);
pushup(k);
}
inline void change(int k,int pos,int x)
{
if (t[k].l==t[k].r){t[k].mn=x;return;}
int mid=(t[k].l+t[k].r)>>1;
if (pos<=mid) change(k<<1,pos,x);
else change(k<<1|1,pos,x);
pushup(k);
}
inline int query(int k,int l,int r)
{
if (l>r) return inf;
if (t[k].l==l&&t[k].r==r) return t[k].mn;
int mid=(t[k].l+t[k].r)>>1;
if (r<=mid) return query(k<<1,l,r);
else if (l>mid) return query(k<<1|1,l,r);
else return min(query(k<<1,l,mid),query(k<<1|1,mid+1,r));
}
int main()
{
n=read();q=read();
F(i,1,n)
{
x=read();a[i]=read();
add_edge(x,i);
f[i][0]=x;
}
rt=1;d[1]=1;dfs(1);
build(1,1,n);
while(q--)
{
char ch=getchar();
while (ch<'A'||ch>'Z') ch=getchar();
x=read();
if (ch=='V')
{
y=read();
change(1,l[x],y);
}
else if (ch=='E') rt=x;
else
{
if (rt==x) printf("%d\n",t[1].mn);
else if (l[x]<=l[rt]&&r[rt]<=r[x])
{
int dis=d[rt]-d[x]-1;y=rt;
F(i,0,20) if ((1<