题目链接

https://www.luogu.org/problemnew/show/P2486

分析

看上去又是一道强行把序列上问题搬运到树上的裸题,然而分析之后发现并不然...

首先我们考虑如何在序列上维护信息:从最简单的想起,如果两个相邻的元素合并,显然是这两个元素所含颜色段个数(其实就是1)加起来,如果两个元素颜色相同就减1;那么两个分别含有两个元素的相邻区间合并,还是把这两个区间所含颜色段个数加起来,如果左区间最右边的颜色等于右区间最左边的颜色就减去1.

如此我们已经得到线段树维护信息的方法,记录区间所含颜色段个数,区间最左边颜色及区间最右边颜色,当然为了\(pushdown\)我们还得维护一个\(tag\)数组表示覆盖标记,然后按上面方法就好了

但是在树链剖分查询两点之间时就与序列上不同了.有一个问题,就是当前链最左边的颜色如果和上面那条链最右边的颜色相等的话,需要将贡献减1.有一个\(naiive\)的方法是每次查询链时再查一下上面那条链最右边的颜色(其实就是单点查询\(fa[top[x]]\)的颜色),然后这个方法看起来不优美,其实有个更妙的方法

我们每次查完一条链后记录该链最左边的颜色,同时将该链最右边的颜色与上一次记录的值比较。看起来很容易但有个问题就是你可能是从\(LCA\)两个不同的子树上向LCA跳,然后从 @ qscqesze_lca 的题解中学到了一个小trick轻易解决了这个问题,请看代码

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <vector>
#define ll long long
#define ri register int
#define ull unsigned long long
using std::min;
using std::max;
using std::swap;
template <class T>inline void read(T &x){
x=0;int ne=0;char c;
while(!isdigit(c=getchar()))ne=c=='-';
x=c-48;
while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
x=ne?-x:x;return ;
}
const int maxn=100005;
const int inf=0x7fffffff;
int n,m;
struct Edge{
int ne,to;
}edge[maxn<<1];
int h[maxn],num_edge=1;
inline void add_edge(int f,int to){
edge[++num_edge].ne=h[f];
edge[num_edge].to=to;
h[f]=num_edge;
}
int col[maxn];
int dep[maxn],fa[maxn],size[maxn],dfn[maxn],rnk[maxn],tot=0,top[maxn],son[maxn];
void dfs_1(int now){
int v;size[now]=1;
for(ri i=h[now];i;i=edge[i].ne){
v=edge[i].to;
if(v==fa[now])continue;
dep[v]=dep[now]+1,fa[v]=now;
dfs_1(v);
size[now]+=size[v];
if(!son[now]||size[v]>size[son[now]])son[now]=v;
}
return ;
}
void dfs_2(int now,int t){
int v;top[now]=t,dfn[now]=++tot,rnk[tot]=now;
if(!son[now])return ;
dfs_2(son[now],t);
for(ri i=h[now];i;i=edge[i].ne){
v=edge[i].to;
if(v==fa[now]||v==son[now])continue;
dfs_2(v,v);
}
return ;
}
int num[maxn<<2],lc[maxn<<2],rc[maxn<<2],tag[maxn<<2];
int L,R,dta;
void build(int now,int l,int r){
tag[now]=-1;
if(l==r){
num[now]=1;
lc[now]=rc[now]=col[rnk[l]];
return ;
}
int mid=(l+r)>>1;
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
num[now]=num[now<<1]+num[now<<1|1]-(rc[now<<1]==lc[now<<1|1]?1:0);
lc[now]=lc[now<<1],rc[now]=rc[now<<1|1];
return ;
}
inline void pushdown(int now){
if(tag[now]!=-1){
tag[now<<1]=tag[now<<1|1]=tag[now];
lc[now<<1]=lc[now<<1|1]=lc[now];
rc[now<<1]=rc[now<<1|1]=rc[now];
num[now<<1]=num[now<<1|1]=1;
tag[now]=-1;
}
return ;
}
void update(int now,int l,int r){
if(L<=l&&r<=R){
num[now]=1;
lc[now]=rc[now]=dta;
tag[now]=dta;
return ;
}
int mid=(l+r)>>1;
pushdown(now);
if(L<=mid)update(now<<1,l,mid);
if(mid<R)update(now<<1|1,mid+1,r);
num[now]=num[now<<1]+num[now<<1|1]-(rc[now<<1]==lc[now<<1|1]?1:0);
lc[now]=lc[now<<1],rc[now]=rc[now<<1|1];
return ;
}
int chain_lc,chain_rc;
int query(int now,int l,int r){
if(L==l)chain_lc=lc[now];
if(R==r)chain_rc=rc[now];
if(L<=l&&r<=R){
return num[now];
}
int ans=0,mid=(l+r)>>1;
pushdown(now);
if(L<=mid&&mid<R)ans=query(now<<1,l,mid)+query(now<<1|1,mid+1,r)-(rc[now<<1]==lc[now<<1|1]?1:0);
else if(L<=mid)ans=query(now<<1,l,mid);
else if(mid<R)ans=query(now<<1|1,mid+1,r);
//num[now]=num[now<<1]+num[now<<1|1]-(rc[now<<1]==lc[now<<1|1]?1:0);
//lc[now]=lc[now<<1],rc[now]=rc[now<<1|1];
return ans;
}
inline void update_path(int x,int y,int c){
dta=c;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
L=dfn[top[x]],R=dfn[x];
update(1,1,n);
x=fa[top[x]];
}
if(dfn[x]>dfn[y])swap(x,y);
L=dfn[x],R=dfn[y];
update(1,1,n);
return ;
}
int lst_1,lst_2;//lst_1总是你当前正在查询的链的上一条链的最左边颜色
inline void query_path(int x,int y){
int ans=0;
lst_1=lst_2=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])
{swap(x,y),swap(lst_1,lst_2);}//非常高明的一个trick,不用刻意查询链top父亲的颜色
L=dfn[top[x]],R=dfn[x];
ans+=query(1,1,n);
if(lst_1==chain_rc)ans--;
lst_1=chain_lc;
x=fa[top[x]];
}
if(dfn[x]<dfn[y])
{swap(x,y),swap(lst_1,lst_2);}
L=dfn[y],R=dfn[x];
ans+=query(1,1,n);
if(chain_rc==lst_1)ans--;
if(chain_lc==lst_2)ans--;
printf("%d\n",ans);
return ;
}
int main(){
int x,y,z;
read(n),read(m);
for(ri i=1;i<=n;i++)read(col[i]);
for(ri i=1;i<n;i++){
read(x),read(y);
add_edge(x,y);
add_edge(y,x);
}
dep[1]=1,fa[1]=0;
dfs_1(1);
dfs_2(1,1);
build(1,1,n);
char opt[5];
while(m--){
scanf("%s",opt);
if(opt[0]=='C'){
read(x),read(y),read(z);
update_path(x,y,z);
}
else{
read(x),read(y);
query_path(x,y);
}
}
return 0;
}

luogu题解P2486[SDOI2011]染色--树链剖分+trick的更多相关文章

  1. Luogu P2486 [SDOI2011]染色(树链剖分+线段树合并)

    Luogu P2486 [SDOI2011]染色 题面 题目描述 输入输出格式 输入格式: 输出格式: 对于每个询问操作,输出一行答案. 输入输出样例 输入样例: 6 5 2 2 1 2 1 1 1 ...

  2. 洛谷 P2486 [SDOI2011]染色 树链剖分

    目录 题面 题目链接 题目描述 输入输出格式 输入格式 输出格式 输入输出样例 输入样例: 输出样例: 说明 思路 PushDown与Update Q AC代码 总结与拓展 题面 题目链接 P2486 ...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. css3网格效果(整理)

    css3网格效果(整理) 一.总结 一句话总结: css3网格原理是渐变(linear-gradient)绘制图形,background-size属性指定重复的小单元的大小 多个渐变(linear-g ...

  2. legend3---11、php前端模块化开发

    legend3---11.php前端模块化开发 一.总结 一句话总结: 把常用的前端块(比如课程列表,比如评论列表)放进模块列表里面,通过外部php变量给数据,可以很好的实现复用和修改 页面调用 @p ...

  3. docker mysql容器的安装使用

    docker拉取镜像的命令为 docker pull image[:TAG] 如果我们想安装的mysql版本不是最新的版本,则需要填充TAG,如果不带上TAG,则默认安装的为最新版本,虽然可以通过do ...

  4. mysql 创建++删除 数据库

    创建RUNOOB数据库,并设定编码集为utf8 CREATE DATABASE IF NOT EXISTS RUNOOB DEFAULT CHARSET utf8 COLLATE utf8_gener ...

  5. 3299 Humidex

    Humidex Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 23219   Accepted: 8264 Descript ...

  6. jdk与jre与jdk都是干什么的有什么区别和jvm详解:

    1.0,jdk与jre和jvm的区别: JDK(Java Development Kit):指的是Java开发工具集.JDK是整个Java的核心,包括了Java基础类库.Java运行环境(JRE)和J ...

  7. 8. find

    find使用 find命令的主要作用是对树形目录层次结构进行彻底检查. find命令的一般格式: find pathname -expressions     find常用表达式 在当前文件夹中找出名 ...

  8. RocketMQ之四:RocketMq事务消息

    事务消息 通过消息的异步事务,可以保证本地事务和消息发送同时执行成功或失败,从而保证了数据的最终一致性. 发送端执行如下几步: 发送prepare消息,该消息对Consumer不可见 执行本地事务(如 ...

  9. 【并行计算-CUDA开发】CUDA编程——GPU架构,由sp,sm,thread,block,grid,warp说起

    掌握部分硬件知识,有助于程序员编写更好的CUDA程序,提升CUDA程序性能,本文目的是理清sp,sm,thread,block,grid,warp之间的关系.由于作者能力有限,难免有疏漏,恳请读者批评 ...

  10. vsphere6.7+Horizon7.8推送桌面遇到的一些问题

    系统环境 esxi6.7+vSphere6+Horizon7.8 VC环境为windows server 2016 standard 问题描述 vc安装 链接外部数据库找不到DSN 安装view co ...