中国国家队训练
问题描述
给定一棵大小为 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 的子树最小值
最开始根为1。
Output
对于每个 Q ,输出子树最小值。
数据规模和约定 对于 10% 的数据:n, Q ≤ 5; 对于其余 20% 的数据:保证是一条链; 对于 100% 的数据:n, Q ≤ 10^5。
这道题经过不断的wa终于AC了,因为这题涉及到换根并且要求子树最小值,所以我们可以先O(N) 求出dfs序,同时O(nlogn)可以预处理出父亲数组,然后对于操作,只需看第三个操作即可,设当前根为root,需要查询x子树,我们可以发现,对于最开始的树,root若在x的子树外,则最小值就是x子树内,否则就是整棵树把root所在的x的儿子的那棵子树去掉之后的最小值。而判断root是否在x的子树内,可以由dfs序看出,然后用线段树维护区间最小值以及单点修改即可。(此题建议打非递归,会爆栈,本人比较懒,所以直接开栈了。)
CODE:
{$m 111111111}
var
f:array[0..100000,0..20] of longint;
num:array[0..200000] of longint;
min:array[0..2000000] of longint;
pre,son:array[0..200000] of longint;
deep,mark,child,fir,last,data,now,father:array[0..100000] of longint;
n,q,tot,kid:longint;
function minn(a,b:longint):longint;
begin if a<b then exit(a); exit(b); end;
procedure add(a,b:longint);
begin
inc(tot); pre[tot]:=now[a]; now[a]:=tot; son[tot]:=b;
inc(tot); pre[tot]:=now[b]; now[b]:=tot; son[tot]:=a;
end;
procedure dfs(x,fa:longint);
var p,val:longint;
begin
if (fa<>1) and (fa<>0) then
mark[x]:=mark[fa];
inc(tot); num[tot]:=x;
fir[x]:=tot;
deep[x]:=deep[fa]+1;
p:=now[x];
val:=0;
f[x,0]:=fa;
while p>-1 do
begin
if son[p]<>fa then
begin
dfs(son[p],x);
inc(val);
mark[son[p]]:=val;
end;
p:=pre[p];
end;
inc(tot); num[tot]:=x;
last[x]:=tot;
end;
procedure update(x:longint);
begin
min[x]:=minn(min[x+x],min[x+x+1]);
end;
procedure build(p,l,r:longint);
var mid:longint;
begin
if l>r then exit;
if l=r then
begin
min[p]:=data[num[l]];
exit;
end;
mid:=(l+r) div 2;
build(p+p,l,mid);
build(p+p+1,mid+1,r);
update(p);
end;
procedure change(p,l,r,x,v:longint);
var mid:longint;
begin
if (l>x) or (r<x) then exit;
if (l=r) and (r=x) then
begin
min[p]:=v;
exit;
end;
mid:=(l+r) div 2;
change(p+p,l,mid,x,v);
change(p+p+1,mid+1,r,x,v);
update(p);
end;
function getfa(x,root:longint):longint;
var long,tmp:longint;
begin
long:=deep[root]-deep[x]-1;
while long>0 do
begin
tmp:=trunc(ln(long)/ln(2));
root:=f[root,tmp];
long:=long-(1 shl (tmp));
end;
exit(root);
end;
function find(p,l,r,ll,rr:longint):longint;
var mid:longint;
begin
if (l=ll) and (r=rr) then exit(min[p]);
mid:=(l+r) div 2;
if mid>=rr then
exit(find(p+p,l,mid,ll,rr))
else
if mid<ll then
exit(find(p+p+1,mid+1,r,ll,rr))
else
exit(minn(find(p+p,l,mid,ll,mid),find(p+p+1,mid+1,r,mid+1,rr)));
end;
procedure init;
var i,j,k,a,root,ans,e:longint;
ch:char;
begin
fillchar(now,sizeof(now),$ff);
readln(n,q); tot:=0; kid:=0; //ans:=maxlongint;
data[0]:=maxlongint;
for i:=1 to n do
begin
readln(a,data[i]);
// ans:=minn(ans,data[i]);
// if data[i]=1 then
// writeln('fuck ',i);
// writeln(ans);
add(i,a);
if a=1 then
begin
inc(kid);
child[kid]:=i;
mark[i]:=kid;
end;
end;
//writeln('fuck ',ans);
tot:=0;
dfs(1,0);
for i:=1 to n do
for j:=1 to 20 do
f[i,j]:=f[f[i,j-1],j-1];
build(1,1,tot);
root:=1;
for i:=1 to q do
begin
//writeln('fuck ',root);
read(ch);
if ch='Q' then
begin
readln(j);
if root=j then writeln(min[1])
else
begin
if ((fir[root]<fir[j]) and (last[root]>last[j]))
or (last[root]<fir[j]) or (fir[root]>last[j]) then
writeln(find(1,1,tot,fir[j],last[j]))
else
writeln(minn(find(1,1,tot,1,fir[getfa(j,root)]-1),find(1,1,tot,last[getfa(j,root)]+1,tot)));
end;
end;
if ch='V' then
begin
readln(j,k);
change(1,1,tot,fir[j],k);
change(1,1,tot,last[j],k);
end;
if ch='E' then
begin
readln(root);
// inc(e);
end;
//writeln(min[1]);
end;
// writeln('fuck ',e);
end;
begin
init;
end.