【题意】
有一棵n个节点的树,每个节点有一种颜色,现在要进行m次操作,操作分两种
C a b c:将节点a到节点b的路径上所有节点染成c色
Q a b:询问节点a到节点b的路径上节点的颜色段的数量,例如说:112211就是3个颜色段
【输入】
第一行n、m
接下来一行n个数字表示n个节点的颜色
接下来n-1行每行表示一条边来描述树
之后的m行每行一次操作
【输出】
对于每次询问,输出一行一个数字表示颜色段的个数
据说是动态树的模板问题……
说到动态树,就学了link-cut tree,无奈没有摸到门路,至今没搞懂怎么搞
作为替代,学习了树链剖分
这类问题为什么不能用寻常的方式解决呢,因为无法把大块大块的点归为一块快速处理
树链剖分就是将其有序化的一种划分方式
简单来说,将树剖成一根一根链,然后用线段树修改查询
线段树是个有序结构,所以需要对节点重标号
对于一棵树的根节点,选择其中子树节点最多的节点加入链中按序标号,然后将这条链上的所有节点拿掉,会产生一片森林
对于森林里的每棵树都进行相同操作,直至森林为空
这样树就被拆成了一条一条的链,并且一条链上的节点标号都是有序的,可以用线段树修改查询
从一个节点返回到根节点,可以从该节点跳到链的顶端,再从链的顶端跳到其父节点,再跳到父节点所在链的顶端……
但是这样会很快吗?最坏情况从根到树的底部需要路过多少条链?
log2(n)条
为什么呢……因为每一条链都将树至少划分成两个规模更小的树
十分容易理解的算法,但是比较长
我这里写错了,写的是每次选择最长的一条链进行剖分,最差n^0.5
program paint;
var
ans,fa,top,x,y,tot,n,m,i,j,k,a,b,c:longint;
color,height,up,go,total,num,dd,root:array [0..100001] of longint;
father:array [0..20,0..100001] of longint;
next,point:array [0..200001] of longint;
sum,lb,rb,left,right:array [0..2000001] of longint;
stop:array [0..2000001] of boolean;
order:char;
procedure swap (var a,b:longint);inline;
var
i:longint;
begin
i:=a;
a:=b;
b:=i;
end;
procedure connect (u,v:longint);inline;
begin
inc(tot);
point[tot]:=v;
next[tot]:=root[u];
root[u]:=tot;
end;
procedure update (now:longint);inline;
begin
lb[left[now]]:=lb[now];
rb[left[now]]:=lb[now];
lb[right[now]]:=rb[now];
rb[right[now]]:=rb[now];
stop[now]:=false;
stop[left[now]]:=true;
stop[right[now]]:=true;
sum[left[now]]:=1;
sum[right[now]]:=1;
end;
procedure insert (c,s,e,l,r,now:longint);
var
mid:longint;
begin
if (l=s)and(r=e) then
begin
lb[now]:=c;
rb[now]:=c;
sum[now]:=1;
stop[now]:=true;
exit;
end;
mid:=(l+r) div 2;
if left[now]=0 then
begin
inc(tot);
left[now]:=tot;
end;
if right[now]=0 then
begin
inc(tot);
right[now]:=tot;
end;
if stop[now] then update(now);
if e<=mid then insert(c,s,e,l,mid,left[now])
else
if s>mid then insert(c,s,e,mid+1,r,right[now])
else
begin
insert(c,s,mid,l,mid,left[now]);
insert(c,mid+1,e,mid+1,r,right[now]);
end;
sum[now]:=sum[left[now]]+sum[right[now]];
if rb[left[now]]=lb[right[now]] then dec(sum[now]);
lb[now]:=lb[left[now]];
rb[now]:=rb[right[now]];
end;
function look (x,l,r,now:longint):longint;
var
mid:longint;
begin
if (x=l)or(stop[now]) then exit(lb[now]);
mid:=(l+r) div 2;
if x<=mid then exit(look(x,l,mid,left[now]))
else exit(look(x,mid+1,r,right[now]));
end;
function find (s,e,l,r,now:longint):longint;
var
ans,mid:longint;
begin
if (s=l)and(e=r) then exit(sum[now]);
if stop[now] then update(now);
mid:=(l+r) div 2;
if e<=mid then exit(find(s,e,l,mid,left[now]))
else
if s>mid then exit(find(s,e,mid+1,r,right[now]))
else
begin
ans:=find(s,mid,l,mid,left[now])+find(mid+1,e,mid+1,r,right[now]);
if rb[left[now]]=lb[right[now]] then dec(ans);
exit(ans);
end;
end;
function lca (a,b:longint):longint;inline;
var
i:longint;
begin
if height[a]<height[b] then swap(a,b);
while height[a]-height[b]<>0 do
a:=father[trunc(ln(height[a]-height[b])/ln(2)),a];
if a=b then exit(a);
i:=20;
repeat
if father[0,a]=father[0,b] then exit(father[0,a]);
while father[i,a]=father[i,b] do dec(i);
a:=father[i,a];
b:=father[i,b];
until false;
end;
procedure addup (now,high:longint);
var
i:longint;
begin
i:=0;
while father[i,now]<>0 do
begin
father[i+1,now]:=father[i,father[i,now]];
inc(i);
end;
height[now]:=high;
total[now]:=1;
go[now]:=0;
i:=root[now];
while i<>0 do
begin
if point[i]<>father[0,now] then
begin
father[0,point[i]]:=now;
addup(point[i],high+1);
if total[point[i]]+1>total[now] then
begin
total[now]:=total[point[i]]+1;
go[now]:=point[i];
end;
end;
i:=next[i];
end;
end;
procedure build (now,top:longint);
var
i:longint;
begin
up[now]:=top;
inc(tot);
num[now]:=tot;
if go[now]<>0 then build(go[now],top);
i:=root[now];
while i<>0 do
begin
if (point[i]<>go[now])and(point[i]<>father[0,now]) then
build(point[i],point[i]);
i:=next[i];
end;
end;
begin
assign(input,'paint.in');
reset(input);
assign(output,'paint.out');
rewrite(output);
read(n,m);
for i:=1 to n do
read(color[i]);
for i:=1 to n-1 do
begin
readln(x,y);
inc(dd[x]);
inc(dd[y]);
connect(x,y);
connect(y,x);
end;
for top:=1 to n do
if dd[top]=1 then break;
addup(top,1);
tot:=0;
build(top,top);
fillchar(stop,sizeof(stop),false);
for i:=1 to n do
insert(color[i],num[i],num[i],1,n,0);
for i:=1 to m do
begin
read(order);
if order='C' then
begin
readln(a,b,c);
fa:=lca(a,b);
while up[a]<>up[fa] do
begin
insert(c,num[up[a]],num[a],1,n,0);
a:=father[0,up[a]];
end;
insert(c,num[fa],num[a],1,n,0);
while up[b]<>up[fa] do
begin
insert(c,num[up[b]],num[b],1,n,0);
b:=father[0,up[b]];
end;
insert(c,num[fa],num[b],1,n,0);
end
else
begin
readln(a,b);
if a=b then
begin
writeln(1);
continue;
end;
fa:=lca(a,b);
ans:=0;
k:=0;
while up[a]<>up[fa] do
begin
ans:=ans+find(num[up[a]],num[a],1,n,0);
if look(num[a],1,n,0)=k then dec(ans);
k:=look(num[up[a]],1,n,0);
a:=father[0,up[a]];
end;
ans:=ans+find(num[fa],num[a],1,n,0);
if look(num[a],1,n,0)=k then dec(ans);
k:=0;
while up[b]<>up[fa] do
begin
ans:=ans+find(num[up[b]],num[b],1,n,0);
if look(num[b],1,n,0)=k then dec(ans);
k:=look(num[up[b]],1,n,0);
b:=father[0,up[b]];
end;
ans:=ans+find(num[fa],num[b],1,n,0);
if look(num[b],1,n,0)=k then dec(ans);
dec(ans);
writeln(ans);
end;
end;
close(input);
close(output);
end.