CF850D Tournament Construction
题目传送门
挺难的一道构造题。
题目大意:
给定
m
m
m 个数的一个非负整数集合,不超过
30
30
30。你需要构造一个竞赛图,满足:所有点的出度去重后等于该集合。
m
≤
31
m≤31
m≤31
前置芝士:
兰道定理(Landau’s Theorem):
设点
i
i
i 的出度为
d
i
d_i
di,那么对于任意
1
≤
i
≤
n
1≤i≤n
1≤i≤n,有
∑
j
=
1
i
d
i
≥
i
×
(
i
−
1
)
2
∑^i_{j=1}d_i≥\frac{i\times(i−1)}{2}
∑j=1idi≥2i×(i−1)。且当
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+2≤uk,那么必然存在点
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;
}