兰道定理(竞赛图)

本文详细阐述了兰道定理,涉及完全图的竞赛图构造、出度序列的性质以及证明过程,重点在于理解定理如何确保特定序列能形成竞赛图的条件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

所谓兰道定理,就是兰道定下的道理

(逃)

解析

每条边被规定了方向的完全图叫做竞赛图
竞赛图中,设每个点的出度为 u i u_i ui
显然有:
∑ u i = n × ( n − 1 ) 2 \sum u_i=\dfrac{n\times(n-1)}{2} ui=2n×(n1)
而兰道定理的内容是:
若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=1ksi>=2k×(k1)

当且仅当k=n时,取等
必要性比较显然,现在的关键是充分性
兰道定理的证明类似于裴蜀定理,是一种构造的方法
先构造出一个竞赛图 T T T ,满足任意一点 i 都只对比自己编号小的点连边,设其出度为 u i u_i ui
显然, u i = i − 1 u_i=i-1 ui=i1
然后考虑如下操作:

  1. 找到第一个位置,使得 s i > u i s_i>u_i si>ui,设为 x x x

  2. 找到最靠后的 y y y ,满足 s x = s y s_x=s_y sx=sy(xy可以相等)

  3. 找到第一个位置,使得 s i < u i s_i<u_i si<ui 设为 z z z

  4. 此时由于 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 uzuy>=2,因此,一定存在第三个点p,使得z连向p且p连向y

  5. 调换 ( 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不变

  6. 不断重复以上流程,直到 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
*/

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值