洛谷 2244

来源:互联网 发布:c盘数据恢复软件 编辑:程序博客网 时间:2024/06/05 20:01

                                                    这道题有很巧妙的结论,但还是难以掩盖它是乱搞题的本质-----By APT

      这道题看起来无从下手,因为关系很复杂,我先给出做法,再给出证明,首先我们将A赢B的关系表示成A-->B的有向边,我们找到出度最大的点,然后找到没有被它直接指向的点,然后这些点都是可能赢的点,我们将它们标记为能赢,然后放入队列,继续上述操作,最终就能得到答案。

      证明:首先由于我们一开始的点是出度最大点,也就是说没有其他点能够一下打败当前点和当前点能直接打败的点,因为如果存在这样的点,它的度数一定大于当前点。那么这样又如何呢?我们考虑剩下的所有点,为什么它们都可能赢呢,因为我们可以构造出一种方式令它们胜利,对于一个剩下的点,我们先让别的点都输掉,然后在让当前点把它能直接打败的点全打败,最后再让这个剩下的点打败当前点,于是它就赢了,然后我们对于每个能赢的点做这种操作,剩下的点都是可能打败当前能赢的点的点,我们先让能赢的点把其他点全打败,再让这个点打败能赢的点,这样它也能赢。于是我们这样一直推下去就可以了。

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define maxn 1000005int que[maxn+5],next[maxn],prev[maxn];int pre[maxn],last[maxn],other[maxn],cd[maxn],l;int n,m,cnt;bool flag[maxn],ans[maxn];int read(void){char ch=getchar();int x=0;while (ch<'0'||ch>'9') ch=getchar();while (ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}return x;}void connect(int x,int y){l++;pre[l]=last[x];last[x]=l;other[l]=y;}int main(){//scanf("%d",&n);n=read();for (int i=1;i<=n;i++) {//scanf("%d",&cd[i]);cd[i]=read();for (int j=1;j<=cd[i];j++) {int a;//scanf("%d",&a);a=read();connect(i,a);}if (cd[i]>cd[que[1]]) que[1]=i;}for (int i=0;i<=n;i++) {prev[i]=i-1;next[i]=i+1;}next[n]=0;pre[0]=0;int h=0,t=1;while (h!=t) {h=h%maxn+1;int u=que[h];for (int p=last[u];p;p=pre[p]) {int v=other[p];flag[v]=1;}int w=next[0];while (w) {if (!flag[w]) {ans[w]=1;cnt++;next[prev[w]]=next[w];prev[next[w]]=prev[w];t=t%maxn+1;que[t]=w;}w=next[w];}for (int p=last[u];p;p=pre[p]) {int v=other[p];flag[v]=0;}}printf("%d",cnt);for (int i=1;i<=n;i++) if (ans[i]) printf(" %d",i);printf("\n");return 0;}


0 0
原创粉丝点击