CF850D Tournament Construction

CF850D Tournament Construction

题目传送门
挺难的一道构造题。
题目大意:
给定 m m m 个数的一个非负整数集合,不超过 30 30 30。你需要构造一个竞赛图,满足:所有点的出度去重后等于该集合。 m ≤ 31 m≤31 m31
前置芝士:
兰道定理(Landau’s Theorem):
设点 i i i 的出度为 d i d_i di,那么对于任意 1 ≤ i ≤ n 1≤i≤n 1in,有 ∑ j = 1 i d i ≥ i × ( i − 1 ) 2 ∑^i_{j=1}d_i≥\frac{i\times(i−1)}{2} j=1idi2i×(i1)。且当 i = n i=n i=n 时取等。
证明请自行百度
竞赛图: 有向完全图。
思路:
由于题中给出的是去重之后的出度集合,我们需要用这个集合构造出一个合法的出度数组 d d d。每个数至少用一次,因此可以做一个类似背包的东西来构造这个 d d d。然后找到第一个满足 d i > u i d_i>u_i di>ui 的位置,然后找到最大的 j j j,满足 u i = u i u_i=u_i ui=ui,接下来找到第一个满足 d k < u k d_k<u_k dk<uk 的位置,有 j < k j<k j<k u j + 2 ≤ u k u_j+2≤u_k uj+2uk,那么必然存在点 x x x 满足 k k k x x x 连边且 x x x j j j 连边。把这两条边翻转即可。这样做只会改变 k k k j j j 的度数,对 x x x 并无影响.
重复上面的过程直到 d d d u u u 完全相同。
代码:

#include<bits/stdc++.h>
#define freo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
#define ll long long
using namespace std;
const int maxn=65,maxm=1835;
int n,m,a[maxn],f[maxn][maxn][maxm],g[maxn][maxn][maxm],d[maxn],u[maxn],e[maxn][maxn];
inline ll read() 
{
	ll ret=0;
	char ch=' ',c=getchar();
	while(!(c<='9'&&c>='0')) ch=c,c=getchar();
	while(c<='9'&&c>='0') ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
	return ch=='-'?-ret:ret;
}
int main() 
{
	m=read();
	for(int i=1; i<=m; i++) a[i]=read();
	sort(a+1,a+m+1);
	f[0][0][0]=1;
	for(int i=1; i<=m; i++)
		for(int j=i; j<maxn; j++)
			for(int k=i-1; k<j; k++)
				for(int y=k*(k-1)/2,x=(j-k)*a[i]+y; x<maxm; x++,y++)
					if(f[i-1][k][y])
						f[i][j][x]=1,g[i][j][x]=j-k;
	n=m;
	while(n<maxn&&!f[m][n][n*(n-1)/2]) n++;
	if(n==maxn) {printf("=(");return 0;}
	printf("%d\n",n);
	for(int i=m,j=n,k=n*(n-1)/2; i>=1; i--) 
	{
		for(int x=0; x<g[i][j][k]; x++) d[j-x]=a[i];
		int tmp=k;
		k-=g[i][j][tmp]*a[i],j-=g[i][j][tmp];
	}
	sort(d+1,d+n+1);
	for(int i=1; i<=n; i++) 
	{
		u[i]=i-1;
		for(int j=1; j<i; j++) e[i][j]=1;
	}
	while(1) 
	{
		int tmp1=1,tmp2=n,tmp3=1,tmp4=n;
		while(tmp1<=n&&d[tmp1]<=u[tmp1]) tmp1++;
		if(tmp1>n) break;
		while(u[tmp2]!=u[tmp1]) tmp2--;
		while(d[tmp3]>=u[tmp3]) tmp3++;
		while(!(e[tmp3][tmp4]&&e[tmp4][tmp2])) tmp4--;
		e[tmp3][tmp4]=0;e[tmp4][tmp3]=1;e[tmp4][tmp2]=0;e[tmp2][tmp4]=1;
		u[tmp3]--;u[tmp2]++;
	}
	for(int i=1; i<=n; i++) 
	{
		for(int j=1; j<=n; j++) putchar(e[i][j]+'0');
		putchar('\n');
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值