[codeforces850D]Tournament Construction

题目大意

给定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<k u[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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值