[codeforces850D]Tournament Construction

来源:互联网 发布:淘宝流量是什么意思啊 编辑:程序博客网 时间:2024/06/04 22:01

题目大意

给定m个数的一个非负整数集合,不超过30。你需要构造一个竞赛图,满足:所有点的出度去重后等于该集合。
m≤31

分析

做这题我用到了兰道定理(Landau’s Theorem)。
设点i的出度为d[i],那么对于任意1in,有ij=1d[i]i(i1)2。且当i=n时是等于。
那么可以考虑构造一个合法的d[]数组。每个数至少用一次,那么可以做一次背包。然后枚举点的个数,判断是否存在即可。
得到出度数组d[]后,依然是利用兰道定理来构造出竞赛图。主要思路如下:
1. 对于i>j,i向j连边,得到一个最初始的竞赛图,记它的出度数组为u[]。并且给d[]升序排序
2. 找到第一个满足d[i]>u[i]的位置,然后找到最大的j,满足u[i]=u[j],接下来找到第一个满足d[k]<u[k]的位置,有j<ku[j]+2u[k],那么必然存在点x满足k向x连边且x向j连边。把这两条边翻转即可
3. 重复上面的过程直到d[]与u[]完全相同
关于兰道定理的具体内容可以上网搜

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int N=62,M=1832;typedef long long LL;int n,m,a[N],f[N][N][M],g[N][N][M],d[N],u[N];bool G[N][N];int main(){    scanf("%d",&m);    for (int i=1;i<=m;i++) scanf("%d",&a[i]);    sort(a+1,a+m+1);    f[0][0][0]=1;    for (int i=1;i<=m;i++)        for (int j=i;j<N;j++)            for (int k=i-1;k<j;k++)                for (int y=k*(k-1)/2,x=(j-k)*a[i]+y;x<M;x++,y++) if (f[i-1][k][y])                {                    f[i][j][x]=1; g[i][j][x]=j-k;                }    for (n=m;n<N && !f[m][n][n*(n-1)/2];n++);    if (n==N)    {        printf("=(\n"); return 0;    }    printf("%d\n",n);    for (int i=m,j=n,k=n*(n-1)/2,x;i;i--)    {        for (x=0;x<g[i][j][k];x++) d[j-x]=a[i];        x=k; k-=g[i][j][x]*a[i]; j-=g[i][j][x];    }    sort(d+1,d+n+1);    for (int i=1;i<=n;i++)    {        u[i]=i-1; for (int j=1;j<i;j++) G[i][j]=1;    }    for (int i,j,k,x;;)    {        for (i=1;i<=n && d[i]<=u[i];i++);        if (i>n) break;        for (j=n;u[j]!=u[i];j--);        for (k=1;d[k]>=u[k];k++);        for (x=n;!(G[k][x] && G[x][j]);x--);        G[k][x]=0; G[x][k]=1; G[x][j]=0; G[j][x]=1; u[k]--; u[j]++;    }    for (int i=1;i<=n;i++)    {        for (int j=1;j<=n;j++) putchar(G[i][j]+'0');        printf("\n");    }    return 0;}
原创粉丝点击