染色(paint)

来源:互联网 发布:梦龙软件官网 编辑:程序博客网 时间:2024/04/27 08:19

【题意】

有一棵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.


原创粉丝点击