所谓兰道定理,就是兰道定下的道理
(逃)
解析
每条边被规定了方向的完全图叫做竞赛图
竞赛图中,设每个点的出度为
u
i
u_i
ui
显然有:
∑
u
i
=
n
×
(
n
−
1
)
2
\sum u_i=\dfrac{n\times(n-1)}{2}
∑ui=2n×(n−1)
而兰道定理的内容是:
若n个点的出度序列升序排序后为
s
i
s_i
si,那么其能构成竞赛图的充要条件是,对于任意的k属于[1,n],都有:
∑
i
=
1
k
s
i
>
=
k
×
(
k
−
1
)
2
\sum_{i=1}^ks_i>=\dfrac{k\times(k-1)}{2}
i=1∑ksi>=2k×(k−1)
当且仅当k=n时,取等
必要性比较显然,现在的关键是充分性
兰道定理的证明类似于裴蜀定理,是一种构造的方法
先构造出一个竞赛图
T
T
T ,满足任意一点 i 都只对比自己编号小的点连边,设其出度为
u
i
u_i
ui
显然,
u
i
=
i
−
1
u_i=i-1
ui=i−1
然后考虑如下操作:
-
找到第一个位置,使得 s i > u i s_i>u_i si>ui,设为 x x x
-
找到最靠后的 y y y ,满足 s x = s y s_x=s_y sx=sy(xy可以相等)
-
找到第一个位置,使得 s i < u i s_i<u_i si<ui 设为 z z z
-
此时由于 u z > s z > = s y > u y u_z > s_z>=s_y>u_y uz>sz>=sy>uy,可以得出, u z − u y > = 2 u_z-u_y>=2 uz−uy>=2,因此,一定存在第三个点p,使得z连向p且p连向y
-
调换 ( z , p ) (z,p) (z,p)和 ( y , p ) (y,p) (y,p)的方向,此时只有 u z u_z uz减小1, u y u_y uy增大1,而 u p u_p up不变
-
不断重复以上流程,直到 u u u和 s s s完全相同
代码
CF850D:Tournament Construction
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+100;
const int mod=1e9+7;
double eps=1e-10;
#define ll long long
ll read(){
ll x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();};
while(isdigit(c)){x=x*10+c-'0';c=getchar();};
return x*f;
}
int n,m;
int dp[35][100][2050],num[35][100][2050];
int a[35],pre[35];
int u[2005],s[2005],tot;
void find(int k,int x,int w){
if(k==0) return;
find(k-1,x-num[k][x][w],w-num[k][x][w]*a[k]);
for(int i=1;i<=num[k][x][w];i++) s[++tot]=a[k];
return;
}
bool jd[2050][2050];
inline void rev(int x,int y){
if(jd[x][y]){
u[x]--;u[y]++;
}
else{
u[y]--;u[x]++;
}
jd[x][y]^=1;jd[y][x]^=1;
}
int main(){
#ifndef ONLINE_JUDGE
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
#endif
n=read();
for(int i=1;i<=n;i++) a[i]=read();
sort(a+1,a+1+n);
dp[0][0][0]=1;
int k(0);
for(k=1;;k++){
for(int i=1;i<=n;i++){
if(i>k) break;
for(int w=k*(k-1)/2;w<=2000;w++){
for(int j=1;j<=k&&j*a[i]<=w;j++){
if(dp[i-1][k-j][w-j*a[i]]){
num[i][k][w]=j;
dp[i][k][w]=1;
}
}
}
}
if(dp[n][k][k*(k-1)/2]) break;
}
//printf("ok k=%d\n",k);
find(n,k,k*(k-1)/2);
//for(int i=1;i<=k/2;i++) swap(s[i],s[k-i+1]);
for(int i=1;i<=k;i++) u[i]=i-1;
for(int i=1;i<=k;i++){
for(int j=1;j<i;j++) jd[i][j]=1;
}
while(1){
int x(0),y(0),z(0),p(0);
//for(int i=1;i<=k;i++) printf("%d ",s[i]);putchar('\n');
//for(int i=1;i<=k;i++) printf("%d ",u[i]);putchar('\n');
for(int i=1;i<=k;i++){
if(s[i]>u[i]){
x=i;break;
}
}
if(!x) break;
y=x;
for(int i=x+1;i<=k;i++) if(u[i]==u[x]) y=x;
for(int i=k;i>=1;i--) if(u[i]>s[i]) z=i;
//printf("x=%d y=%d z=%d\n",x,y,z);
for(p=1;p<=k;p++){
if(jd[z][p]&&!jd[y][p]){
//printf("x=%d y=%d z=%d p=%d\n",x,y,z,p);
rev(z,p);rev(y,p);break;
}
}
}
printf("%d\n",k);
for(int i=1;i<=k;i++){
for(int j=1;j<=k;j++){
printf("%d",jd[i][j]);
}
putchar('\n');
}
return 0;
}
/*
2 3
7 4 9 9
1 2 8
3 1
4 2 4
*/