1 条题解

  • 0
    @ 2025-8-24 21:35:33

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar I_am_Accepted
    我的心脏不再跳动了。

    搬运于2025-08-24 21:35:33,当前版本为作者最后更新于2022-07-12 19:55:43,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    为啥机房里 dalao 们都不屑一顾。

    这里有最严谨的证明(自己推的,有疏漏请指出)。

    我们把人看作点,xx 必然胜 yy 看作 xyx\to y 的有向边,我们称点是「好的」当且仅当这个点可能取得最终胜利,设 N(x)N(x) 表示 xx 点走一步能到达的点集,VV 为点的全集,称一个点 xx 的「反集」为 VxN(x)V-x-N(x)


    首先我们证明出度最大的点(可能不止一个)是好的。

    反证法。假设出度最大的点之一 xx 不好。我们这样构造比赛顺序:

    1. xx 的反集之间对战(随便打),决出最后的赢家设为 yy

    2. yy 依次与 N(x)N(x) 中的点对战(若 yyN(x)N(x) 中某点间没边,让 N(x)N(x) 那头赢),设赢家为 zz

    zyz\ne y,则 zN(x)z\in N(x),最后让 xx 打败 zz 即可,最后赢家 xx 是好的,矛盾。

    z=yz=y,则 N(x)N(y)N(x)\subseteq N(y),由于 xx 出度的最大性,得到 N(x)N(y)|N(x)|\ge |N(y)|,进一步 N(x)=N(y)N(x)=N(y),得到 x,yx,y 之间没有边,最后让 xx 打败 y(=z)y(=z) 即可,矛盾。

    综上,原命题得证。


    其次我们证明若一个点是好的,则其反集也都是好的。

    xx 的反集中有 yy,我们先让 xx 成为 yy 除外的最后赢家,然后 yy 再打败 xx 即可。


    实现:

    我们找到出度最大的点,然后向反集 BFS 即可,过程中用并查集优化可至 O(n+m)O(n+m)mm 为边数)。


    最后我们证明我们找到好的点以外都是不好的。

    设我们通过刚刚方法找到的好的集合为 SS。当一个点 xSx\notin S 第一次与 SS 中的人对战的时候,由 SS 的定义,xx 必输,推出 xx 不是好的。


    完结撒花上代码。

    //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;}
    
    • 1

    信息

    ID
    1187
    时间
    1000ms
    内存
    125MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者