所谓兰道定理,就是兰道定下的道理
(逃)
解析
每条边被规定了方向的完全图叫做竞赛图
竞赛图中,设每个点的出度为uiu_iui
显然有:
∑ui=n×(n−1)2\sum u_i=\dfrac{n\times(n-1)}{2}∑ui=2n×(n−1)
而兰道定理的内容是:
若n个点的出度序列升序排序后为sis_isi,那么其能构成竞赛图的充要条件是,对于任意的k属于[1,n],都有:
∑i=1ksi>=k×(k−1)2\sum_{i=1}^ks_i>=\dfrac{k\times(k-1)}{2}i=1∑ksi>=2k×(k−1)
当且仅当k=n时,取等
必要性比较显然,现在的关键是充分性
兰道定理的证明类似于裴蜀定理,是一种构造的方法
先构造出一个竞赛图 TTT ,满足任意一点 i 都只对比自己编号小的点连边,设其出度为uiu_iui
显然,ui=i−1u_i=i-1ui=i−1
然后考虑如下操作:
-
找到第一个位置,使得si>uis_i>u_isi>ui,设为 xxx
-
找到最靠后的 yyy ,满足 sx=sys_x=s_ysx=sy(xy可以相等)
-
找到第一个位置,使得 si<uis_i<u_isi<ui 设为 zzz
-
此时由于uz>sz>=sy>uyu_z > s_z>=s_y>u_yuz>sz>=sy>uy,可以得出,uz−uy>=2u_z-u_y>=2uz−uy>=2,因此,一定存在第三个点p,使得z连向p且p连向y
-
调换(z,p)(z,p)(z,p)和(y,p)(y,p)(y,p)的方向,此时只有uzu_zuz减小1,uyu_yuy增大1,而upu_pup不变
-
不断重复以上流程,直到uuu和sss完全相同
代码
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);#endifn=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
*/