题解:P8519 [IOI 2021] 钥匙
直接对于每个点做是
每次只从联通块内的这个根节点向外扩展,然后合并联通块(如果一个联通块找不到其他联通块了,我们就在以后不对于它进行操作了),可以做到类似于 Boruvka 的时间复杂度。最终对于每个联通块都有一个可能成为答案的点,这个点在这个联通块之内的肯定是最优的,已知联通块的根节点
时间复杂度
#include<bits/stdc++.h>
#include "keys.h"
#define pb emplace_back
#define fi first
#define se second
#define mp make_pair
using namespace std;
typedef long long ll;
typedef vector<int> vi;
const int maxn=3e5+10;
void cmax(int &x,int y){ x=x>y?x:y; }
void cmin(int &x,int y){ x=x<y?x:y; }
int n,m,num,a[maxn],b[maxn],vis[maxn],vis2[maxn]; queue<int> q;
vector<pair<int,int> > G[maxn];
vi ans,A,vec,cur,col[maxn];
struct DSU{
int fa[maxn];
void init(){ for(int i=1;i<=n;i++) fa[i]=i; }
int find(int u){ return fa[u]==u?u:fa[u]=find(fa[u]); }
void merge(int u,int v){ fa[u]=find(v); vis[find(v)]=1; }
}dsu;
void init(){
while(q.size()) q.pop();
for(auto z:vec) col[z].clear(),b[z]=0;
cur.clear(); vec.clear();
}
void modify(){
if(cur.size()<num) ans=cur,num=cur.size();
else if(cur.size()==num) for(auto u:cur) ans.pb(u);
}
void bfs(int s){
q.push(s);
while(q.size()){
int u=q.front(); q.pop();
if(dsu.find(u)!=s){ dsu.merge(s,u); return ; }
if(vis[u]) continue;
vis[u]=1; cur.pb(u);
if(!b[a[u]]){
b[a[u]]=1; vec.pb(a[u]);
for(auto v:col[a[u]]) q.push(v);
}
for(auto z:G[u]){
int v=z.fi,w=z.se;
if(b[w]) q.push(v);
else col[w].pb(v),vec.pb(w);
}
}
vis2[s]=1; modify();
}
vi find_reachable(vi R,vi U,vi V,vi C){
n=R.size(); m=U.size(); dsu.init(); num=n+1;
for(int i=1;i<=n;i++) a[i]=R[i-1]+1;
for(int i=1;i<=m;i++){
int u=U[i-1]+1,v=V[i-1]+1,w=C[i-1]+1;
G[u].pb(v,w); G[v].pb(u,w);
}
while(1){
bool flag=0; memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++){
if(dsu.find(i)!=i||vis[i]||vis2[i]) continue;
init(); bfs(i); flag=1;
}
if(!flag) break;
}
A.clear(); A.resize(n,0);
for(auto u:ans) A[u-1]=1;
return A;
}