YbtOJ-染色计划【树链剖分,线段树,tarjan】

1|0正题


1|1题目大意

给出n个点的一棵树,每个点有个颜色ai,你每次可以选择一个颜色全部变成另一个颜色。

求最少多少次操作可以把一种颜色变成一个完整的连通块。

1kn2×105


1|2解题思路

考虑如果我们要把一个颜色变成一个联通块,那么首先得把它目前包含它颜色点的最小联通子图全都同化,并且同化这些颜色之后还有可能需要同化其他更多颜色。

这是一个类似于跑图的过程,我们可以考虑建边,如果颜色A需要颜色B那么AB,最后跑出来的图我们找一个点能走到的点数最少即可。

至于怎么优化这个建图的过程,我们树链剖分+线段树上维护每个节点,然后每个节点连向对应的颜色。

然后对于一种颜色我们按照点的dfs序排序,然后收尾相连相邻的点之间的路径拼起来恰好是它颜色的生成子图的两倍,之间用树链剖分去连边就好了。

至于建好图之后跑个tarjan就能求出答案了

时间复杂度:O(nlog2n)


1|3code

#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<stack> using namespace std; const int N=2e5+10,M=N*5; struct node{ int to,next; }a[N<<1]; int n,k,cnt,tot,c[N],ls[N],fa[N],siz[N]; int seg[N],id[N],top[N],son[N],dep[N]; int num,dfc,p[M],dfn[M],low[M],col[M],f[M],in[M]; bool ins[N];stack<int> s;vector<int> v[N],G[M]; void addl(int x,int y){ a[++tot].to=y; a[tot].next=ls[x]; ls[x]=tot;return; } void dfs1(int x){ dep[x]=dep[fa[x]]+1;siz[x]=1; for(int i=ls[x];i;i=a[i].next){ int y=a[i].to; if(y==fa[x])continue; fa[y]=x;dfs1(y);siz[x]+=siz[y]; if(siz[y]>siz[son[x]])son[x]=y; } return; } void dfs2(int x){ seg[++cnt]=x;id[x]=cnt; if(son[x]){ top[son[x]]=top[x]; dfs2(son[x]); } for(int i=ls[x];i;i=a[i].next){ int y=a[i].to; if(y==fa[x]||y==son[x])continue; top[y]=y;dfs2(y); } return; } void Build(int x,int L,int R){ p[x]=++cnt; if(L==R){G[p[x]].push_back(c[seg[L]]);return;} int mid=(L+R)>>1; Build(x*2,L,mid);Build(x*2+1,mid+1,R); G[p[x]].push_back(p[x*2]); G[p[x]].push_back(p[x*2+1]); return; } void Change(int x,int L,int R,int l,int r,int vp){ if(L==l&&R==r){G[vp].push_back(p[x]);return;} int mid=(L+R)>>1; if(r<=mid)Change(x*2,L,mid,l,r,vp); else if(l>mid)Change(x*2+1,mid+1,R,l,r,vp); else Change(x*2,L,mid,l,mid,vp),Change(x*2+1,mid+1,R,mid+1,r,vp); return; } void Recovery(int x,int y,int s){ // printf("%d %d %d\n",x,y,s); while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); Change(1,1,n,id[top[x]],id[x],s);x=fa[top[x]]; } if(dep[x]>dep[y])swap(x,y); Change(1,1,n,id[x],id[y],s); return; } void tarjan(int x){ dfn[x]=low[x]=++dfc; ins[x]=1;s.push(x); for(int i=0;i<G[x].size();i++){ int y=G[x][i]; if(!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); } else if(ins[y]) low[x]=min(low[x],dfn[y]); } if(low[x]==dfn[x]){ int y;++num; do{ y=s.top();s.pop();ins[y]=0; f[num]+=(y<=k);col[y]=num; }while(y!=x); } return; } bool cmp(int x,int y) {return id[x]<id[y];} int main() { freopen("color.in","r",stdin); freopen("color.out","w",stdout); scanf("%d%d",&n,&k); for(int i=1;i<n;i++){ int x,y; scanf("%d%d",&x,&y); addl(x,y);addl(y,x); } for(int i=1;i<=n;i++) scanf("%d",&c[i]),v[c[i]].push_back(i); dfs1(1);top[1]=1;dfs2(1); cnt=k;Build(1,1,n); for(int i=1;i<=k;i++){ if(v[i].size()<2)continue; sort(v[i].begin(),v[i].end(),cmp); for(int j=1;j<v[i].size();j++) Recovery(v[i][j-1],v[i][j],i); } for(int i=1;i<=cnt;i++) if(!dfn[i])tarjan(i); for(int x=1;x<=cnt;x++){ for(int i=0;i<G[x].size();i++){ int y=G[x][i]; if(col[x]==col[y])continue; in[col[x]]++; } } int ans=k; for(int i=1;i<=k;i++) if(!in[col[i]])ans=min(ans,f[col[i]]); printf("%d\n",ans-1); return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/15890232.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(48)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示