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.

时间: 2024-10-25 22:55:45

BZOJ 2243:染色(树链剖分+区间合并线段树)的相关文章

BZOJ 2243: [SDOI2011]染色 树链剖分 倍增lca 线段树

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=2243 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写

【BZOJ4515】游戏,树链剖分+永久化标记线段树维护线段信息(李超线段树)

Time:2016.05.10 Author:xiaoyimi 转载注明出处谢谢 传送门 思路: 李超线段树 一开始听faebdc讲,并没有听的很懂ww 后来找到良心博文啊有木有 折越 首先可以把修改转换一下,因为那个dis非常不爽.显然s~t的路径有s~lca和lca~t组成.令d[x]表示x的深度,对于s~lca上面的点,修改的值相当于a*(d[s]-d[x])+b=-a*d[x]+(b-a*d[s]),lca~t上面的点的值相当于a*(d[s]+d[x]-2*d[lca])+b=a*d[x

bzoj2243树链剖分+区间合并

树链上区间合并的问题比区间修改要复杂,因为每一条重链在线段树上分布一般都是不连续的,所以在进行链上操作时要手动将其合并起来,维护两个端点值 处理时的方向问题:lca->u是一个方向,lca->v是另一个方向,到最后合并这两个放向时都看左端点即可 #include<cstring> #include<string> #include<iostream> #include<queue> #include<cstdio> #include&

[GDOI2016] 疯狂动物园 [树链剖分+可持久化线段树]

题面 太长了,而且解释的不清楚,我来给个简化版的题意: 给定一棵$n$个点的数,每个点有点权,你需要实现以下$m$个操作 操作1,把$x$到$y$的路径上的所有点的权值都加上$delta$,并且更新一个版本 操作2,对于有向路径$(x,y)$上的点$a_i$,求下面的和值: $\sum_{i=1}^{len} a_i \sum_{j=1}^{len-i} j$ 操作3,回到第$i$个版本(但是下一次更新以后还是到总版本号+1的那个版本) 思路 这个题显然一眼就是树剖+可持久化数据结构啊 那么核心

【树链剖分】【线段树】bzoj3626 [LNOI2014]LCA

引用题解: http://blog.csdn.net/popoqqq/article/details/38823457 题目大意: 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LCA(i,z)].(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和) 这题看见了直接卡壳...然后

codeforces 343D Water Tree 树链剖分 dfs序 线段树 set

题目链接 这道题主要是要考虑到同一棵子树中dfs序是连续的 然后我就直接上树剖了... 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAXN=600005; 4 5 struct Node 6 { 7 int l,r; 8 int value; 9 void init() 10 { 11 l=r=value=0; 12 } 13 }tree[4*MAXN]; 14 vector<int>nei[MAXN]

【块状树】【树链剖分】【线段树】bzoj3531 [Sdoi2014]旅行

离线后以宗教为第一关键字,操作时间为第二关键字排序. <法一>块状树,线下ac,线上tle…… #include<cstdio> #include<cmath> #include<algorithm> #include<cstring> #include<queue> using namespace std; queue<int>q; int f,c; inline void R(int &x){ c=0;f=1;

【BZOJ3681】Arietta 树链剖分+可持久化线段树优化建图+网络流

[BZOJ3681]Arietta Description Arietta 的命运与她的妹妹不同,在她的妹妹已经走进学院的时候,她仍然留在山村中.但是她从未停止过和恋人 Velding 的书信往来.一天,她准备去探访他.对着窗外的阳光,临行前她再次弹起了琴.她的琴的发声十分特殊.让我们给一个形式化的定义吧.所有的 n 个音符形成一棵由音符 C ( 1 号节点) 构成的有根树,每一个音符有一个音高 Hi .Arietta 有 m 个力度,第 i 个力度能弹出 Di 节点的子树中,音高在 [Li,R

(树链剖分+区间合并)HYSBZ - 2243 染色

题意: 两个操作: 1.把一条树链上的所有点权值变为w. 2.查询一条树链上有多少个颜色段 分析: 一看就是区间合并,做这到题首先需要一定的区间合并基础, 不过这题合并这部分在线段树区间合并中已经算是非常的简单的了. 线段树部分没有难度. 那么难点在于,在往LCA上走的时候,我们如何进行区间合并. 本来我想着, 在向上走的时候顺便进行区间判断并且合并,但是似乎有问题. 其实,可以将两步分开,先算出区间没合并之前的颜色段数,再次进行Top,判断颜色是否相等,相等就减掉. 代码: 1 #includ