[SDOI2011]染色题解
作者:互联网
[SDOI2011]染色题解
今天的bug实在改不出来,写道之前做的题的题解
洛谷题目链接
这道题与noi2021的第一题轻重边几乎一模一样,把这道题代码稍微修一下就可以过那道题
[noi2021轻重边题目链接](https://www.luogu.com.cn/problem/P7735)
建议写完这道题把如上题目也写了
正文
很容易想到暴力解法,我们考虑优化,很明显的区间修改所以使用线段树或者树状数组优化,但是需要在树上操作所以我们需要使这个区间有序可以修改,所有需要使用树链剖分使用剖分序从中建线段树,注意,修改并不是直接修改而是找公共祖先的过程中修改有序的序列。
其实画个图很好理解但是画图太麻烦了,所以我不画了。
就此修改算是结束了。
接下来是查询,我们同样需要找最近公共祖先的过程中,查询,接下来是本题这关键点。
查询过程中我们需要合并的两个序列其中左序列的右端点和右序列的左端点,设suml为左序列的颜色段数,sumr为有序列的颜色段数,l为左序列的右端点,r为右序列的左端点。
sum=suml+sumr;
if(l==r)sum--;
解释:因为合并的两个区间相交的位置为同一个颜色所以sum多加了1,因此需要减1.
源代码如下
#include<bits/stdc++.h>
using namespace std;
const int MN=1e5+100;
int n,m;
struct tree{
int l,r,lc,rc,sum;
}t[MN<<2];
int color[MN],head[MN],cnt;
struct node{
int nxt,to;
}e[MN<<1];
inline void add(int a,int b){
e[++cnt].nxt=head[a],head[a]=cnt,e[cnt].to=b;
}
int fa[MN],dep[MN],siz[MN],son[MN];
void dfs1(int now,int pre){
dep[now]=dep[pre]+1;
fa[now]=pre;
siz[now]=1;
for(int i=head[now];i;i=e[i].nxt){
int to=e[i].to;
if(to==pre)continue;
dfs1(to,now);
siz[now]+=siz[to];
if(siz[to]>siz[son[now]]){
son[now]=to;
}
}
}
int top[MN],dfn[MN],tot,name[MN];
void dfs2(int now,int pre){
top[now]=pre;
dfn[now]=++tot;
name[tot]=now;
if(son[now]){
dfs2(son[now],pre);
}
for(int i=head[now];i;i=e[i].nxt){
int to=e[i].to;
if(to==fa[now]||to==son[now])continue;
dfs2(to,to);
}
}
int tag[MN<<2];
inline void pushup(int id){
t[id].sum=t[id<<1].sum+t[id<<1|1].sum;
if(t[id<<1].rc==t[id<<1|1].lc)t[id].sum--;
t[id].lc=t[id<<1].lc,t[id].rc=t[id<<1|1].rc;
}
inline void pushdown(int id){
if(tag[id]==0)return;
t[id<<1].sum=1,t[id<<1|1].sum=1,tag[id<<1]=tag[id],tag[id<<1|1]=tag[id];
t[id<<1].lc=t[id<<1].rc=tag[id],t[id<<1|1].lc=t[id<<1|1].rc=tag[id];
tag[id]=0;
}
void build(int id,int l,int r){
t[id].l=l,t[id].r=r;
if(l==r){
t[id].lc=t[id].rc=color[name[l]];
t[id].sum=1;
return;
}
int mid=(l+r)>>1;
build(id<<1,l,mid),build(id<<1|1,mid+1,r);
pushup(id);
}
void update(int id,int l,int r,int c){
if(t[id].l>=l&&t[id].r<=r){
t[id].sum=1;
t[id].lc=c;
t[id].rc=c;
tag[id]=c;
return;
}
int mid=(t[id].l+t[id].r)>>1;
pushdown(id);
if(l<=mid)update(id<<1,l,r,c);
if(r>=mid+1)update(id<<1|1,l,r,c);
pushup(id);
}
void change(int a,int b,int c){
while(top[a]!=top[b]){
if(dep[top[a]]<dep[top[b]])swap(a,b);
update(1,dfn[top[a]],dfn[a],c);
a=fa[top[a]];
}
if(dep[a]<dep[b])swap(a,b);
update(1,dfn[b],dfn[a],c);
}
int get_sum(int id,int l,int r){
if(t[id].l>=l&&t[id].r<=r){
return t[id].sum;
}
int mid=(t[id].l+t[id].r)>>1,ans=0;
pushdown(id);
if(l<=mid)ans+=get_sum(id<<1,l,r);
if(r>=mid+1)ans+=get_sum(id<<1|1,l,r);
if(l<=mid&&r>=mid+1)if(t[id<<1].rc==t[id<<1|1].lc)ans--;
return ans;
}
int get_color(int id,int k){
if(t[id].l==t[id].r){
return t[id].lc;
}
int mid=(t[id].l+t[id].r)>>1;
pushdown(id);
if(k<=mid)return get_color(id<<1,k);
else return get_color(id<<1|1,k);
}
int query(int a,int b){
int ans=0,nowr,nowl;
while(top[a]!=top[b]){
if(dep[top[a]]<dep[top[b]])swap(a,b);
ans+=get_sum(1,dfn[top[a]],dfn[a]);
nowr=get_color(1,dfn[top[a]]);
nowl=get_color(1,dfn[fa[top[a]]]);
a=fa[top[a]];
if(nowr==nowl)ans--;//这个地方容易忽略,链头与其父亲颜色一样时需要减1
}
if(dep[a]<dep[b])swap(a,b);
ans+=get_sum(1,dfn[b],dfn[a]);
return ans?ans:1;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%d",&color[i]);
}
for(int i=1;i<n;++i){
int a,b;
scanf("%d%d",&a,&b);
add(a,b),add(b,a);
}
dfs1(1,0);
dfs2(1,1);
build(1,1,n);
while(m--){
int a,b,c;
char x;
scanf("%s%d%d",&x,&a,&b);
if(x=='Q'){
printf("%d\n",query(a,b));
}
else{
scanf("%d",&c);
change(a,b,c);
}
}
return 0;
}
标签:int,题解,sum,MN,SDOI2011,染色,序列,now,id 来源: https://www.cnblogs.com/fanner-Blog/p/15084807.html