BZOJ 2243:染色(树链剖分+区间合并线段树)
[SDOI2011]染色
Description
给定一棵有n个节点的无根树和m个操作,操作有2类:
1、将节点a到节点b路径上所有点都染成颜色c;
2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”、“222”和“1”。
请你写一个程序依次完成这m个操作。
Input
第一行包含2个整数n和m,分别表示节点数和操作数;
第二行包含n个正整数表示n个节点的初始颜色
下面 行每行包含两个整数x和y,表示x和y之间有一条无向边。
下面 行每行描述一个操作:
“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括a和b)都染成颜色c;
“Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。
Output
对于每个询问操作,输出一行答案。
Sample Input
6 5
2 2 1 2 1 1
1 2
1 3
2 4
2 5
2 6
Q 3 5
C 2 1 1
Q 3 5
C 5 1 2
Q 3 5
Sample Output
3
1
2
HINT
数N<=10^5,操作数M<=10^5,所有的颜色C为整数且在[0, 10^9]之间。
分析:
树链剖分后用线段树维护数据,还是比较好的一道题。
树链剖分的第一个dfs要求出各个点的深度d,父节点fa,子树节点数sz,重儿子son,第二个dfs求出每个点在序列中的位置id,所在路径顶端节点编号top。
第二个dfs就是用来剖分路径的,要注意必须先对重儿子处理,只有这样一条路径的点在序列上才是连续的,这样才能用线段树维护。
然后就是线段树维护了, 不要忘记修改x到y的权值要用id[x]和id[y]表示,另外注意任何对线段树的操作必须先下传懒惰标记。
在查询时由于要跨过多条路径,相邻两个路径相接的地方颜色相同的情况也要判断。
代码:
program putcolor; type thing=record l,r,v,s,e:longint; end; point=^node; node=record x:longint; next:point; end; var a:array[0..100000]of point; dat:array[0..400000]of thing; sz,son,id,fa,v,b,d,top:array[0..100000]of longint; n,i,m,len:longint; procedure add(x,y:longint); var p:point; begin new(p); p^.x:=y; p^.next:=a[x]; a[x]:=p; end; procedure swap(var x,y:longint); var t:longint; begin t:=x; x:=y; y:=t; end; procedure dfs1(x:longint); var y:longint; p:point; begin new(p); p:=a[x]; sz[x]:=1; while p<>nil do begin if p^.x=fa[x] then begin p:=p^.next; continue; end; y:=p^.x; fa[y]:=x; d[y]:=d[x]+1; dfs1(y); inc(sz[x],sz[y]); if sz[y]>sz[son[x]] then son[x]:=y; p:=p^.next; end; end; procedure dfs2(x,k:longint); var i:longint; p:point; begin inc(len); id[x]:=len; b[len]:=v[x]; top[x]:=k; if son[x]>0 then dfs2(son[x],k); new(p); p:=a[x]; while p<>nil do begin if (p^.x<>fa[x])and(p^.x<>son[x]) then dfs2(p^.x,p^.x); p:=p^.next; end; end; procedure down(p,x,y:longint); var l,r,v:longint; begin if x=y then exit; l:=p*2; r:=p*2+1; v:=dat[p].e; dat[l].l:=v; dat[l].r:=v; dat[l].s:=1;dat[l].e:=v; dat[r].l:=v; dat[r].r:=v; dat[r].s:=1;dat[r].e:=v; dat[p].e:=-1; end; procedure update(p,x,y:longint); var l,r:longint; begin if x=y then exit; l:=p*2; r:=p*2+1; dat[p].l:=dat[l].l; dat[p].r:=dat[r].r; dat[p].s:=dat[l].s+dat[r].s; if dat[l].r=dat[r].l then dec(dat[p].s); end; procedure build(p,l,r:longint); var mid:longint; begin dat[p].s:=0; dat[p].e:=-1; if l=r then exit; mid:=(l+r) div 2; build(p*2,l,mid); build(p*2+1,mid+1,r); end; procedure change(x,y,p,l,r,v:longint); var mid:longint; begin if dat[p].e>=0 then down(p,l,r); if (x<=l)and(r<=y) then begin dat[p].l:=v; dat[p].r:=v; dat[p].s:=1; dat[p].e:=v; end else begin mid:=(l+r) div 2; if x<=mid then change(x,y,p*2,l,mid,v); if y>mid then change(x,y,p*2+1,mid+1,r,v); update(p,l,r); end; end; function query(x,y,p,l,r:longint):longint; var mid,ans:longint; begin if (x<=l)and(r<=y) then exit(dat[p].s) else begin mid:=(l+r) div 2;if dat[p].e>=0 then down(p,l,r); ans:=0; if x<=mid then ans:=query(x,y,p*2,l,mid); if y>mid then inc(ans,query(x,y,p*2+1,mid+1,r)); if (x<=mid)and(y>mid) then if dat[p*2].r=dat[p*2+1].l then dec(ans); exit(ans); end; end; function get(p,l,r,x:longint):longint; var mid:longint; begin if dat[p].e>=0 then down(p,l,r); if l=x then exit(dat[p].l); mid:=(l+r) div 2; if x<=mid then exit(get(p*2,l,mid,x)); if x>mid then exit(get(p*2+1,mid+1,r,x)); end; procedure solvechange(x,y,z:longint); var fx,fy,t,ans:longint; begin fx:=top[x]; fy:=top[y]; ans:=0; while fx<>fy do begin if d[fx]<d[fy] then begin swap(x,y); swap(fx,fy);; end; change(id[fx],id[x],1,1,n,z); x:=fa[fx]; fx:=top[x]; end; if d[x]>d[y] then swap(x,y); change(id[x],id[y],1,1,n,z); end; function solvequery(x,y:longint):longint; var fx,fy,t,ans:longint; begin fx:=top[x]; fy:=top[y]; ans:=0; while fx<>fy do begin if d[fx]<d[fy] then begin swap(x,y); swap(fx,fy); end; inc(ans,query(id[fx],id[x],1,1,n)); if get(1,1,n,id[fx])=get(1,1,n,id[fa[fx]]) then dec(ans); x:=fa[fx]; fx:=top[x]; end; if d[x]>d[y] then swap(x,y); inc(ans,query(id[x],id[y],1,1,n)); exit(ans); end; procedure solve; var i,x,y,z:longint; c:char; begin build(1,1,n); for i:=1 to n do change(id[i],id[i],1,1,n,v[i]); for i:=1 to m do begin read(c); if c='C' then begin read(x,y,z); solvechange(x,y,z); end else begin read(x,y); writeln(solvequery(x,y)); end; readln; end; end; procedure int; var i,x,y:longint; p:point; begin readln(n,m); for i:=1 to n do read(v[i]); for i:=1 to n-1 do begin readln(x,y); add(x,y); add(y,x); end; end; begin int; dfs1(1);dfs2(1,1); solve; end.