[解题记录] [SDOI2011]染色
[SDOI2011]染色#
题意简述#
给定一棵 个节点的无根树,共有 个操作,操作分为两种:
- 将节点 到节点 的路径上的所有点(包括 和 )都染成颜色 。
- 询问节点 到节点 的路径上的颜色段数量。
颜色段的定义是极长的连续相同颜色被认为是一段。例如 112221
由三段组成:11
、222
、1
。
解题思路#
操作一就是区间覆盖,那么重点就要放在操作二了,我们可以首先想想再序列上怎么做,我们肯定要维护的就是一个区间颜色段的数和懒标记了,但这样并不能支持我们完成区间的合并操作,因为如果合并的区间中间的两个端点颜色相同那么颜色段数就要减一,所以我们可以再维护一段区间的左端点的颜色和右端点的颜色,有了这些,序列上的操作就完成了。
有了维护序列的基础,就可以想树上怎么维护,由于树剖的特性,我们求路径上的答案是分段求的,那么我们肯定要知道每段相接的端点的颜色是否一样,这样才不会多算,现在我们可以模拟一下:
从上图可以看到,有两种情况,分别是不在同一条链时和在同一条链时,对于第一种情况,我们每次跳的时候都要和之前的跳过来的点判断一下(就是图中的 和 ),如果简单记录上一次的 我们就还会发现一个问题,我们不知道是左边的链还是右边的链往上跳的,所以我们需要用 和 去存当前链上一次的颜色,如果两条链对换了,就是 了,我们也相应的 ,从图中还能发现在链上的情况我们需要判断两次
Code#
#include <bits/stdc++.h>
#define ls p<<1
#define rs p<<1|1
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int N=4e5+10,INF=1e9+10;
int ver[N],tot,nxt[N],head[N];
int fa[N],dep[N],dfn[N],top[N],sz[N],dfstime,son[N],rk[N];
int lazy[N],ret[N],n,m,w[N];
struct node{
int l,r,ans,tag;
}t[N];
void add(int x,int y){
ver[++tot]=y,nxt[tot]=head[x],head[x]=tot;
ver[++tot]=x,nxt[tot]=head[y],head[y]=tot;
}
void pushup(int p){
if(t[ls].r==t[rs].l) t[p].ans=t[ls].ans+t[rs].ans-1;
else t[p].ans=t[ls].ans+t[rs].ans;
t[p].l=t[ls].l;t[p].r=t[rs].r;
}
void dfs1(int u,int father){
fa[u]=father;sz[u]=1;dep[u]=dep[father]+1;
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(v==fa[u]) continue;
dfs1(v,u);
sz[u]+=sz[v];
if(sz[v]>sz[son[u]]) son[u]=v;
}
}
void dfs2(int u,int anc){
top[u]=anc;dfn[u]=++dfstime;rk[dfn[u]]=u;
if(son[u]) dfs2(son[u],anc);
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(top[v]) continue;
dfs2(v,v);
}
}
void build(int p,int l,int r){
if(l==r){
t[p].l=t[p].r=w[rk[l]];
t[p].ans=1;
return;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(p);
}
void down(int p){
if(!t[p].tag) return;
t[ls].ans=t[rs].ans=1;
t[ls].l=t[ls].r=t[ls].tag=t[p].tag;
t[rs].l=t[rs].r=t[rs].tag=t[p].tag;
t[p].tag=0;
return;
}
void upd(int p,int l,int r,int x,int y,int k){
if(x<=l&&y>=r){
t[p].l=t[p].r=t[p].tag=k;
t[p].ans=1;
return;
}
down(p);
int mid=(l+r)>>1;
if(x<=mid) upd(ls,l,mid,x,y,k);
if(y>mid) upd(rs,mid+1,r,x,y,k);
pushup(p);
}
void chainupd(int x,int y,int z){
while(top[x]^top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
upd(1,1,n,dfn[top[x]],dfn[x],z);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
upd(1,1,n,dfn[x],dfn[y],z);
return;
}
node query(int p,int l,int r,int x,int y){
if(x<=l&&y>=r) return t[p];
down(p);
int mid=(l+r)>>1;
if(x>mid) return query(rs,mid+1,r,x,y);
if(y<=mid) return query(ls,l,mid,x,y);
node res;
node t1=query(ls,l,mid,x,y);
node t2=query(rs,mid+1,r,x,y);
res.ans=t1.ans+t2.ans;
if(t1.r==t2.l) res.ans--;
res.l=t1.l;res.r=t2.r;
return res;
}
int chainq(int x,int y){
int res=0,lstx=0,lsty=0;
while(top[x]^top[y]){
if(dep[top[x]]<dep[top[y]]){
swap(x,y);
swap(lstx,lsty);
}
node tmp=query(1,1,n,dfn[top[x]],dfn[x]);
res+=tmp.ans-(tmp.r==lstx);
lstx=tmp.l;
x=fa[top[x]];
}
if(dep[x]>dep[y]){
swap(x,y);
swap(lstx,lsty);
}
node tmp=query(1,1,n,dfn[x],dfn[y]);
res+=tmp.ans-(lsty==tmp.r)-(lstx==tmp.l);
return res;
}
int main(){
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;++i) cin>>w[i];
for(int i=1;i<n;++i){
int x,y;
cin>>x>>y;
add(x,y);
}
dfs1(1,0);
dfs2(1,1);
build(1,1,n);
for(int i=1;i<=m;++i){
string s;
cin>>s;
if(s=="C"){
int a,b,c;
cin>>a>>b>>c;
chainupd(a,b,c);
}
else{
int a,b;
cin>>a>>b;
printf("%d\n",chainq(a,b));
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现