[JOISC2020] 首都
作者:互联网
题意(建议看原题面)
\(n\)个点,\(k\)种颜色,每个点有一个颜色\(c_i\)。一次操作可以合并两种颜色。问最少多少次操作可以使存在一种颜色,把该颜色的点提取出来是联通的,换句话说该颜色的点两两之间(路径)不经过其它颜色。
思路
-
先口胡一下我不太想写的倍增优化建图+Tarjan,贺了firm的代码进行学习。
我想起来我之前做过一道倍增优化建图+网络流的题。
连边的思路我考场上也想到了的,相同颜色\(i\)的两个节点路径上经过另一个颜色\(j\)的节点,就将\(i\)向\(j\)连边,相当于,颜色\(i\)需要把颜色\(j\)合并进来。
如果\(j->u\),\(i->u\)发现这是传递性的,因此考虑缩点,显然答案出度为0的点sz-1的最小值。
类似虚树的感觉,枚举颜色,提取出该颜色的点。按dfn排序,可以称按dfn排序后相邻的点在树上是相邻的(lca)最深
排序后相邻的求lca,倍增优化建图(两端往lca上跳)。
最后再跑Tarjan即可。 -
还有之前老板讲过的点分治做法,比较好写。
把分治中心的颜色当做首都,找它子树里面同样颜色的节点往上到中心的路径上出现的新颜色,加入队列。
每次取出队首颜色,发现如果该颜色有节点出现在该子树外,直接结束。
因为子树里到子树外的那个点的lca做分治中心不会更差(因为存在在路径中,当前分治中心一定连向它)。 -
code:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,k,nxt[N],to[N],head[N],ecnt,c[N];
int sz[N],rt,smx[N],ans;
vector<int> V[N];
bool mark[N],vis[N],col[N];
void add_edge(int u,int v) {nxt[++ecnt]=head[u];to[ecnt]=v;head[u]=ecnt;}
void gt_sz(int u,int fa) {
sz[u]=1;
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];if(v==fa||mark[v])continue;
gt_sz(v,u);
sz[u]+=sz[v];
}
}
void gt_rt(int u,int fa,int tot) {
smx[u]=0;
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];if(v==fa||mark[v])continue;
gt_rt(v,u,tot);
smx[u]=max(smx[u],sz[v]);
}
smx[u]=max(smx[u],tot-sz[u]);
if(smx[u]<smx[rt])rt=u;
}
int tp,st[N],st2[N],tp2,f[N];
void dfs(int u,int fa) {
vis[u]=1;st[++tp]=u;f[u]=fa;
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];if(v==fa||mark[v])continue;
dfs(v,u);
}
}
queue<int> Q; //the color we need
void Clear() {
while(!Q.empty())Q.pop();
while(tp) {vis[st[tp--]]=0;}
while(tp2) {col[st2[tp2--]]=0;}
}
void solve(int u) {
dfs(u,0);
Q.push(c[u]);col[c[u]]=1;st2[++tp2]=c[u];
bool flag=1;
int res=0;
while(flag&&!Q.empty()) {
int cc=Q.front();Q.pop();
res++;
for(int i=0;i<V[cc].size();i++) {
int x=V[cc][i],cf=c[f[x]];
if(!vis[x]) {Clear();return;}
if(!cf)continue;
if(!col[cf]) {
col[cf]=1;st2[++tp2]=cf;
Q.push(cf);
}
}
}
ans=min(ans,res-1);
Clear();
}
void Divide(int u,int tot) {
rt=0;gt_rt(u,0,tot);
mark[u=rt]=1;gt_sz(u,0);
solve(u);
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];if(mark[v])continue;
Divide(v,sz[v]);
}
}
int main() {
smx[0]=1e9;
scanf("%d%d",&n,&k);
for(int i=1;i<n;i++) {int u,v;scanf("%d%d",&u,&v);add_edge(u,v),add_edge(v,u);}
for(int i=1;i<=n;i++) {scanf("%d",&c[i]);V[c[i]].push_back(i);}
ans=k-1;
gt_sz(1,0);Divide(1,n);
printf("%d",ans);
return 0;
}
标签:sz,head,颜色,smx,int,void,JOISC2020,首都 来源: https://www.cnblogs.com/bestime/p/16482760.html