P2244 选举预测
I_am_Accepted · · 题解
为啥机房里 dalao 们都不屑一顾。
这里有最严谨的证明(自己推的,有疏漏请指出)。
我们把人看作点,
首先我们证明出度最大的点(可能不止一个)是好的。
反证法。假设出度最大的点之一
-
让
x 的反集之间对战(随便打),决出最后的赢家设为y 。 -
将
y 依次与N(x) 中的点对战(若y 与N(x) 中某点间没边,让N(x) 那头赢),设赢家为z 。
若
若
综上,原命题得证。
其次我们证明若一个点是好的,则其反集也都是好的。
设
实现:
我们找到出度最大的点,然后向反集 BFS 即可,过程中用并查集优化可至
最后我们证明我们找到好的点以外都是不好的。
设我们通过刚刚方法找到的好的集合为
完结撒花上代码。
//We'll be counting stars.
#include<bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define For(i,j,k) for(int i=(j),i##_=(k);i<=i##_;i++)
#define N 1000010
vector<int> e[N],id;
int f[N],n;
bool b[N];
queue<int> q;
inline int gf(int x){ return x==f[x]?x:f[x]=gf(f[x]); }
inline void del(int x){ f[x]=gf(x+1),q.push(x); }
int main(){
scanf("%d",&n);
int k,x,mx=-1,cnt=n;
For(i,1,n){
scanf("%d",&k);
if(k>mx) id.clear(),mx=k;
if(k==mx) id.pb(i);
while(k--) scanf("%d",&x),e[i].pb(x);
}
For(i,1,n+1) f[i]=i;
for(int i:id) del(i);
while(!q.empty()){
x=q.front();
q.pop();
for(int i:e[x]) b[i]=true;
for(int i=gf(1);i<=n;i=gf(i+1)) if(!b[i]) del(i);
for(int i:e[x]) b[i]=false;
}
for(int i=gf(1);i<=n;i=gf(i+1)) b[i]=1,cnt--;
printf("%d",cnt); For(i,1,n) if(!b[i]) printf(" %d",i);
return 0;}