题解:AT_abc401_e [ABC401_e] Reachable Set
原题传送门
首先如果说对于某个
考虑从小到大利用并查集维护连通性。从小到大枚举每个点,把这个点所有连向编号小于等于
直接暴力检查是
遍历队列元素,如果当前元素和 break。
显然一个点只要跟
只要确定有解就很简单了。所有与小于等于
维护多少个点直接相连较为简单,可以见代码部分。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3000100;
int n,m,tot;
struct edge{
int nxt,to;
}e[N*2];
bool vis[N]; //记录这个点有没有计入答案的计算
int ans;
int head[N],cnt;
inline void add(int u,int v){
e[++cnt].nxt=head[u];
e[cnt].to=v;
head[u]=cnt;
}
int f[N];
queue <int> q;
int find(int x){
if(f[x]==x) return x;
else return f[x]=find(f[x]);
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
add(u,v); add(v,u);
}
for(int i=1;i<=n;i++){
if(vis[i]) ans--; //清除这个点对答案的影响
q.push(i);
for(int j=head[i];j;j=e[j].nxt){
int v=e[j].to;
if(v<=i){
int U=find(i),V=find(v);
if(U!=V) f[U]=V;
}else if(!vis[v]){
vis[v]=true;
ans++;
}
}
while(!q.empty()){
int u=q.front();
if(find(u)!=find(1)) break;
q.pop();
}
if(!q.empty()) cout<<-1<<'\n';
else cout<<ans<<'\n';
}
return 0;
}