【算法笔记】竞赛图(有向完全图)(相关题型总结)

整理的算法模板合集: ACM模板


目录

  • 竞赛图(有向完全图)
    • 一、兰道定理
    • 例题HDU 5873 Football Games
    • 二、求竞赛图的任意三元环
    • 三、求竞赛图的哈密顿回路数量的期望

竞赛图(有向完全图)

竞赛图也叫有向完全图。每对顶点之间都有一条边相连的有向图称为竞赛图

竞赛图的一些简单的性质:

  • 竞赛图没有自环,没有二元环;若竞赛图存在环,则一定存在三元环。(如果存在一个环大于三元,那么一定存在另一个三元的小环。)
  • 任意竞赛图都有哈密顿路径(经过每个点一次的路径,不要求回到出发点)。
  • 图存在哈密顿回路的充要条件是强联通。
  • 哈密顿问题中,对于 n 阶竞赛图,当 n 大于等于 2 时一定存在哈密顿通路

设 D 为 n 阶有向简单图,若 D 的基图为 n 阶无向完全图,则 D 为 n 阶竞赛图。

简单来说,竞赛图就是将完全无向图的无向边给定了方向。

一、兰道定理

兰道定理(Landau’s Theorem)是用来判定竞赛图的定理。
将一个竞赛图的每一个点的出度从小到大排序后得到的序列称为竞赛图的比分序列

【算法笔记】竞赛图(有向完全图)(相关题型总结)_第1张图片

例题HDU 5873 Football Games

题目大意:
现在有比赛,所有队伍两两进行比赛,赢的积2分,输的积0分,如果平局的话就各自都积1分,现在给出每只队伍的得分情况,判断是否合法。

思路:
给出的 m 个队伍可以构成一个竞赛图,问得分情况是否合法实质是在问竞赛图是否合法
根据竞赛图的兰道定理,将得分视为比分序列,将所有得分进行排序,然后依次处理:由于每次比赛胜利都会使得总分 +2,那么前 i 只队伍的得分情况必须大于等于 i*(i-1),当判断到最后一只队伍时,有前 n-1 只队伍的得分必须大于 n*(n-1)

int a[N];
int main(){
    int t;
    while(scanf("%d",&t)!=EOF){
        while(t--){
            int n;
            scanf("%d",&n);
            for(int i=1;i<=n;i++)
                scanf("%d",&a[i]);
            sort(a+1,a+1+n);
 
            int sum=0;
            bool flag=true;
            for(int i=1;i<=n;i++){
                sum+=a[i];
                if(i<=n-1){
                    if(sum<i*(i-1)){
                        flag=false;
                        break;
                    }
                }
                else{
                    if(sum!=i*(i-1)){
                        flag=false;
                        break;
                    }
                }
            }
 
            if(flag)
                printf("T\n");
            else
                printf("F\n");
        }
    }
    return 0;
}

来源

二、求竞赛图的任意三元环

CF117C Cycle(dfs爆搜)
题目传送门

在这里插入图片描述

因为竞赛图(有向完全图)如果存在环的话就一定存在三元环
我们直接暴力dfs即可,如果x到y,y能到x的fa,那么fa,x,y三个点就形成了一个三元环。如果找到三元环就直接退出。

const int N = 5007, M = 5000007, INF = 0x3f3f3f3f;
int n, m;
char s[N][N];

bool vis[N];

bool dfs(int x , int fa)
{
    vis[x] = 1;
    for(int i = 1; i <= n; ++ i){
        if(s[x][i] - '0'){
            if(s[i][fa] - '0'){
                printf("%d %d %d\n", fa, x, i);
                return true;
            }
            if(!vis[i]){
                if(dfs(i, x))
                    return true;
            }
        }
    }
    return false;
}

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; ++ i)
    scanf("%s", s[i] + 1);

    for(int i =1; i <= n; ++ i)
        if(!vis[i])
        if(dfs(i, i))return 0;
    puts("-1");
    return 0;
}

三、求竞赛图的哈密顿回路数量的期望

luogu P4233 射命丸文的笔记 分治NTT+竞赛图

【算法笔记】竞赛图(有向完全图)(相关题型总结)_第2张图片
竞赛图存在哈密顿回路的充要条件就是强连通
f ( n ) f(n) f(n) 表示 n n n 个点形成强连通竞赛图的方案数,一个简单的容斥就是

f ( n ) = 2 ( n 2 ) − ∑ i = 1 n − 1 ( n i ) f ( i ) 2 ( n − i 2 ) f(n)=2^{\binom{n}{2}}-\sum_{i=1}^{n-1}{\binom{n}{i}f(i)2^{\binom{n-i}{2}}} f(n)=2(2n)i=1n1(in)f(i)2(2ni)

考虑分子怎么求。我们先钦定一个环,然后剩余边随便连,可以发现这样实际上是在算所有哈密顿回路出现次数之和。
于是答案就是
( n − 1 ) ! 2 ( ( n 2 ) − n ) f ( n ) \frac{{\left(n-1\right)}!{2}^{\left({\binom{n}{2}-n}\right)}}{f(n)} f(n)(n1)!2((2n)n)
求f的话直接分治 n t t \tt ntt ntt 就可以了

题解来源

#define rep(i,st,ed) for (int i=st;i<=ed;++i)
typedef long long LL;
const int MOD=998244353;
const int N=800005;

LL f[N],g[N],A[N],B[N],fac[N],inv[N];
LL wn1[N],wn2[N];
int rv[N],n;

void upd(LL &x,LL v) {
	x+=v,(x>=MOD)?(x-=MOD):0;
}

LL ksm(LL x,LL dep) {
	LL res=1;
	for (;dep;dep>>=1,x=x*x%MOD) {
		(dep&1)?(res=res*x%MOD):0;
	}
	return res;
}

void NTT(LL *a,int n,int f) {
	for (int i=0;i<n;++i) if (i<rv[i]) std:: swap(a[i],a[rv[i]]);
	for (int i=1;i<n;i<<=1) {
		LL wn=((f==1)?wn1[i]:wn2[i]);
		for (int j=0;j<n;j+=(i<<1)) {
			LL w=1;
			for (int k=0;k<i;++k,w=w*wn%MOD) {
				LL u=a[j+k],v=a[j+k+i]*w%MOD;
				a[j+k]=u+v,(a[j+k]>=MOD)?(a[j+k]-=MOD):0;
				a[j+k+i]=u-v,(a[j+k+i]<0)?(a[j+k+i]+=MOD):0;
			}
		}
	}
	if (f==-1) {
		LL ny=ksm(n,MOD-2);
		for (int i=0;i<n;++i) a[i]=a[i]*ny%MOD;
	}
}

void solve(int l,int r) {
	if (l==r) {
		if (l) f[l]=(g[l]+MOD-f[l])*fac[l]%MOD;
		return ;
	}
	int mid=(l+r)>>1;
	solve(l,mid);
	int len=1,lg=0; for (;len<=(r-l+1)*2;) len<<=1,lg++;
	for (int i=0;i<len;++i) {
		rv[i]=(rv[i>>1]>>1)|((i&1)<<(lg-1));
		A[i]=0,B[i]=g[i];
	}
	rep(i,l,mid) A[i-l]=f[i]*inv[i]%MOD;
	NTT(A,len,1),NTT(B,len,1);
	for (int i=0;i<len;++i) A[i]=A[i]*B[i]%MOD;
	NTT(A,len,-1);
	rep(i,mid+1,r) upd(f[i],A[i-l]);
	solve(mid+1,r);
}

int main(void) {
	fac[0]=fac[1]=inv[0]=inv[1]=1;
	for (int i=1;i<N;i<<=1) {
		wn1[i]=ksm(3,(MOD-1)/i/2);
		wn2[i]=ksm(3,MOD-1-(MOD-1)/i/2);
	}
	rep(i,2,1e5) {
		fac[i]=fac[i-1]*i%MOD;
		inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
	}
	rep(i,2,1e5) inv[i]=inv[i-1]*inv[i]%MOD;
	scanf("%d",&n);
	rep(i,1,n) g[i]=ksm(2,(LL)i*(i-1)/2)*inv[i]%MOD;
	solve(0,n);
	puts("1\n-1");
	rep(i,3,n) {
		LL ans=fac[i-1]*ksm(2,(LL)i*(i-3)/2)%MOD;
		ans=ans*ksm(f[i],MOD-2)%MOD;
		printf("%lld\n", ans);
	}
	return 0;
}

你可能感兴趣的:(《ACM模板》,图论,-,特殊的图(仙人掌,竞赛图,弦图))