点分治。
考虑当前的分治重心的城市被完全联通。
这可以用队列接解决。每次放入一种城市,就把那些城镇的父亲加入队列,如果存在城镇不在当前分治重心的联通块内,那么说明必定存在另一个分治重心能算到它,直接退出即可。
剩下的和模板没有任何区别。
复杂度 \(O(n\log n)\)。
code:
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,k,co[N],f[N],siz[N],vis[N],vis2[N],vis3[N],tmp[N],fa[N],rt,ans=INT_MAX/2,tot=0,cnt=0;
vector<int>adj[N],c[N];
void dfs(int u,int lst,int sum){
siz[u]=1;f[u]=0;
for(int i=0;i<adj[u].size();++i){
int v=adj[u][i];if(v==lst||vis[v])continue;
dfs(v,u,sum);siz[u]+=siz[v];
f[u]=max(f[u],siz[v]);
}
f[u]=max(f[u],sum-siz[u]);
if(f[u]<f[rt])rt=u;
}
void dfs2(int u,int lst){
tmp[++tot]=u;vis2[u]=1;fa[u]=lst;
for(int i=0;i<adj[u].size();++i){
int v=adj[u][i];if(v==lst||vis[v])continue;
dfs2(v,u);
}
}
queue<int>q;
bool check(int w){
for(int i=0;i<c[w].size();++i){
int v=c[w][i];if(!vis2[v])return true;
q.push(v);
}
++cnt;
return false;
}
void cal(int u){
while(!q.empty())q.pop();
vis3[co[u]]=1;
if(check(co[u]))return;
while(!q.empty()){
int u=q.front();q.pop();
if(!vis3[co[fa[u]]]){
vis3[co[fa[u]]]=1;
if(check(co[fa[u]]))return;
}
}
ans=min(ans,cnt-1);
}
void solve(int u){
vis[u]=1;tot=0;cnt=0;dfs2(u,u);cal(u);
for(int i=1;i<=tot;++i)vis2[tmp[i]]=vis3[co[tmp[i]]]=0;
for(int i=0;i<adj[u].size();++i){
int v=adj[u][i];if(vis[v])continue;
rt=0;dfs(v,u,siz[v]);solve(rt);
}
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n>>k;
for(int i=1;i<n;++i){
int u,v;cin>>u>>v;
adj[u].push_back(v);adj[v].push_back(u);
}
for(int i=1;i<=n;++i){
cin>>co[i];
c[co[i]].push_back(i);
}
f[0]=1e9;dfs(1,0,n);solve(rt);
cout<<ans<<endl;
return 0;
}
标签:co,int,题解,分治,back,push,P7215
From: https://www.cnblogs.com/HQJ2007/p/17561380.html