点此看题面

大致题意: 有一棵\(n\)个节点的无根树和\(m\)个操作,且每个节点有一个颜色。操作有两种:一种是将两点树上路径之间所有点染成颜色\(c\),另一种是询问两点树上路径之间颜色段的数量。

树链剖分

这道题的核心算法应该是树链剖分

一个简单的小问题

先让我们来思考一个简单的小问题。

对于两个区间,我们该如何求出这两个区间合并后有多少个颜色段?

事实上,我们只需知道这两个区间中每个区 颜色段的数量\(Sum\)最左端的颜色\(Left\)最右端的颜色\(Right\),就可以得到合并后新区间的信息了。

我们只需要比较 左区间的\(Right\) 和右区间的\(Left\),若两个颜色相同,则合并后的新区间颜色段数量为 左区间的\(Sum+\)右区间的\(Sum-1\)(如果出现这样的情况,左区间与右区间合并后的中间部分会连接成一个颜色段,因此要减\(1\)),否则,合并后的新区间颜色段数量就为 左区间的\(Sum+\)右区间的\(Sum\)

而合并后区间的\(Left\)即为 左区间的\(Left\),\(Right\)即为 右区间的\(Right\)(这应该还是比较好理解的吧)。

这样一来,我们就解决如何更新区间信息的问题了。

用树剖解决此题

这样一来,就不难想到怎么用树链剖分解决此题了。

我们可以用一棵线段树来维护树剖后的序列的信息。

然后就用 树剖求两点树上路径信息 的做法来询问即可。

一个需要注意的地方就是,在树剖求两点树上路径信息的过程中,信息的合并也要采用上面的方法。

其实这还是一道挺裸的题。

代码

#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define abs(x) ((x)<0?-(x):(x))
#define LL long long
#define ull unsigned long long
#define swap(x,y) (x^=y,y^=x,x^=y)
#define N 100000
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
using namespace std;
int n,ee=0,a[N+5],lnk[N+5];
struct edge
{
int to,nxt;
}e[2*N+5];
class FIO
{
private:
#define Fsize 100000
#define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
#define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,FoutSize,stdout),Fout[(FoutSize=0)++]=ch))
int f,FoutSize,OutputTop;char ch,Fin[Fsize],*FinNow,*FinEnd,Fout[Fsize],OutputStack[Fsize];double w;
public:
FIO() {FinNow=FinEnd=Fin;}
inline void read(int &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));x*=f;}
inline void read_char(char &x) {while(isspace(x=tc()));}
inline void read_string(string &x) {x="";while(isspace(ch=tc()));while(x+=ch,!isspace(ch=tc()));}
inline void write(int x) {if(!x) return (void)pc('0');if(x<0) pc('-'),x=-x;while(x) OutputStack[++OutputTop]=x%10+48,x/=10;while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;}
inline void write_char(char x) {pc(x);}
inline void write_string(string x) {register int i,len=x.length();for(i=0;i<len;++i) pc(x[i]);}
inline void end() {fwrite(Fout,1,FoutSize,stdout);}
}F;
class TreeChainDissection//树链剖分
{
private:
#define PushUp(x) (node[x]=node[x<<1]+node[x<<1|1])
#define PushDown(x) (~flag[x]?(node[x<<1]=node[x<<1|1]=key(1,flag[x<<1]=flag[x],flag[x<<1|1]=flag[x]),flag[x]=-1):0)
int d,fa[N+5],son[N+5],sz[N+5],Top[N+5],dfn[N+5],fac[N+5],Depth[N+5],flag[N<<2];
struct key//存储区间信息
{
int Sum,l,r;
key(int x=0,int y=-1,int z=-1):Sum(x),l(y),r(z){}
inline bool empty() {return !(Sum|(~l)|(~r));}//判断区间是否为空
}node[N<<2];
inline friend key operator + (key x,key y)//区间信息合并
{
if(x.empty()) return y;//如果x为空,返回y
if(y.empty()) return x;//如果y为空,返回x
return key(x.Sum+y.Sum-!(x.r^y.l),x.l,y.r);//否则,将两个区间信息合并
}
inline void dfs1(int x)
{
register int i;
for(sz[x]=1,i=lnk[x];i;i=e[i].nxt)
{
if(!(fa[x]^e[i].to)) continue;
fa[e[i].to]=x,Depth[e[i].to]=Depth[x]+1,dfs1(e[i].to),sz[x]+=sz[e[i].to];
if(sz[e[i].to]>sz[son[x]]) son[x]=e[i].to;
}
}
inline void dfs2(int x,int col)
{
register int i;
if(son[fac[dfn[x]=++d]=x]) dfs2(son[x],col);
for(Top[x]=col,i=lnk[x];i;i=e[i].nxt)
{
if(!(fa[x]^e[i].to&&son[x]^e[i].to)) continue;
dfs2(e[i].to,e[i].to);
}
}
inline void Build(int l,int r,int rt)//建树
{
flag[rt]=-1;
if(!(l^r)) return (void)(node[rt]=key(1,a[fac[l]],a[fac[l]]));
register int mid=l+r>>1;
Build(l,mid,rt<<1),Build(mid+1,r,rt<<1|1),PushUp(rt);
}
inline void Update(int l,int r,int rt,int ul,int ur,int x)//修改
{
if(ul<=l&&r<=ur) return (void)(node[rt]=key(1,flag[rt]=x,x));
register int mid=l+r>>1;
PushDown(rt);//下推标记
if(ul<=mid) Update(l,mid,rt<<1,ul,ur,x);
if(ur>mid) Update(mid+1,r,rt<<1|1,ul,ur,x);
PushUp(rt);//上传子节点信息
}
inline key Query(int l,int r,int rt,int ql,int qr)//询问操作
{
if(ql<=l&&r<=qr) return node[rt];
register int mid=l+r>>1;register key res;
PushDown(rt);//下推标记
if(ql<=mid) res=Query(l,mid,rt<<1,ql,qr);
if(qr>mid) res=res+Query(mid+1,r,rt<<1|1,ql,qr);
PushUp(rt);
return res;//返回区间信息
}
public:
inline void Init(int rt) {dfs1(fa[rt]=rt),dfs2(rt,1),Build(1,n,1);}
inline void Update_Path(int l,int r,int x)//修改两点树上路径信息
{
while(Top[l]^Top[r])
{
if(Depth[Top[l]]<Depth[Top[r]]) swap(l,r);
Update(1,n,1,dfn[Top[l]],dfn[l],x),l=fa[Top[l]];
}
dfn[l]<dfn[r]?Update(1,n,1,dfn[l],dfn[r],x):Update(1,n,1,dfn[r],dfn[l],x);
}
inline int Query_Path(int l,int r)//询问两点树上路径信息
{
register int res=0,t1=-1,t2=-1;register key t;
while(Top[l]^Top[r])
{
if(Depth[Top[l]]<Depth[Top[r]]) swap(l,r),swap(t1,t2);
t=Query(1,n,1,dfn[Top[l]],dfn[l]),res+=t.Sum-!(t.r^t1),t1=t.l,l=fa[Top[l]];//注意采用区间信息合并的方式来更新信息
}
return (res+(dfn[l]<dfn[r]?((t=Query(1,n,1,dfn[l],dfn[r])).Sum-!(t1^t.l)-!(t2^t.r)):((t=Query(1,n,1,dfn[r],dfn[l])).Sum-!(t2^t.l)-!(t1^t.r))));
}
}S;
int main()
{
register int i,Q,x,y,z;register char op;
for(F.read(n),F.read(Q),i=1;i<=n;++i) F.read(a[i]);
for(i=1;i<n;++i) F.read(x),F.read(y),add(x,y),add(y,x);
for(S.Init(1);Q;--Q)
{
F.read_char(op),F.read(x),F.read(y);
if(op^'Q') F.read(z),S.Update_Path(x,y,z);
else F.write(S.Query_Path(x,y)),F.write_char('\n');
}
return F.end(),0;
}

【BZOJ2243】[SDOI2011] 染色(树链剖分)的更多相关文章

  1. bzoj2243[SDOI2011]染色 树链剖分+线段树

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 9012  Solved: 3375[Submit][Status ...

  2. BZOJ2243[SDOI2011]染色——树链剖分+线段树

    题目描述 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段), 如“112221 ...

  3. 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树

    [BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...

  4. bzoj-2243 2243: [SDOI2011]染色(树链剖分)

    题目链接: 2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6267  Solved: 2291 Descript ...

  5. BZOJ2243 洛谷2486 [SDOI2011]染色 树链剖分

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ2243 题目传送门 - 洛谷2486 题意概括 一棵树,共n个节点. 让你支持以下两种操作,共m次操 ...

  6. BZOJ 2243: [SDOI2011]染色 [树链剖分]

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6651  Solved: 2432[Submit][Status ...

  7. Bzoj 2243: [SDOI2011]染色 树链剖分,LCT,动态树

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 5020  Solved: 1872[Submit][Status ...

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

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/pr ...

  9. BZOJ 2243: [SDOI2011]染色 树链剖分+线段树区间合并

    2243: [SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数 ...

  10. 2243: [SDOI2011]染色(树链剖分+线段树)

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 8400  Solved: 3150[Submit][Status ...

随机推荐

  1. java二分法查找实现代码

    package util; class BinarySearch { static int binarySearch(int[] array,int goal){//传入排好序的数组和目标数字 int ...

  2. Exadata中的dbserver_backup.sh脚本

    dbserver_backup.sh脚本在老版本的exadata中,它存放在/opt/oracle.SupportTools目录中,主要用于/根文件系统和/boot分区的备份.dbserver_bac ...

  3. chrome安装文件点击没有反应(收藏用)

    备份Chrome浏览器用户数据 关闭Chrome浏览器,用Windows资源管理器打开%LOCALAPPDATA%\Google,复制Chrome文件夹到其它目录.   打开程序和功能管理功能 按下W ...

  4. Jmeter分布式(转)jmeter -n -t baidu.jmx -l result.jtl -R 172.16.20.146:1099

    1.准备2台在同一个局域网内的机器,一台作为master主控机,一台作为slave 执行机 master机器ip:172.16.20.134 slave机器ip:172.16.20.146 2.在2台 ...

  5. B - Average Gym - 101161B 组合数学

    http://codeforces.com/gym/101161/attachments 今天被卡常了,其实是自己对组合数技巧研究的不够. 如果是n, m <= 1e5的,然后取模是质数,那么可 ...

  6. 【Unity3D/C#】Unity3D中的Coroutine详解

    Unity中的coroutine是通过yield expression;来实现的.官方脚本中到处会看到这样的代码. 疑问: yield是什么? Coroutine是什么? unity的coroutin ...

  7. 3 - Selenium元素定位和操作

    3.1定位 <button id="gbqfba" aria-label="Google Search" name="btnK" cl ...

  8. 浅谈堆-Heap(一)

    应用场景和前置知识复习 堆排序 排序我们都很熟悉,如冒泡排序.选择排序.希尔排序.归并排序.快速排序等,其实堆也可以用来排序,严格来说这里所说的堆是一种数据结构,排序只是它的应用场景之一 Top N的 ...

  9. Visual Studio中的引用项目和直接引用DLL文件

    在VS中引用类库时有多种方法,其中用的最多的就是在引用时选择项目选项卡引用本解决方案下的类库项目和选择浏览选项卡直接引用类库DLL文件,实际上这两种引用方式略有不同,今天就为大家总结下. C#本地项目 ...

  10. 一般处理程序ashx

    在用户列表中链接数据库 在这里Html只管做Post 请求,其他逻辑全部交给ashx 其中修改需要隐藏域 1.展现列表 public void ProcessRequest (HttpContext ...