2018-ICPC(青岛)L-Sub-cycle Graph(组合数学)

题目

Given an undirected simple graph with n (n ≥ 3) vertices and m edges where the vertices are numbered from 1 to n, we call it a "sub-cycle" graph if it's possible to add a non-negative number of edges to it and turn the graph into exactly one simple cycle of n vertices.

Given two integers n and m, your task is to calculate the number of different sub-cycle graphs with n vertices and m edges. As the answer may be quite large, please output the answer modulo 109 + 7.

Recall that

  • A simple graph is a graph with no self loops or multiple edges;
  • A simple cycle of n (n ≥ 3) vertices is a connected undirected simple graph with n vertices and n edges, where the degree of each vertex equals to 2;
  • Two undirected simple graphs with n vertices and m edges are different, if they have different sets of edges;
  • Let u < v, we denote (u, v) as an undirected edge connecting vertices u and v. Two undirected edges (u1, v1) and (u2, v2) are different, if u1 ≠ u2 or v1 ≠ v2.

input

There are multiple test cases. The first line of the input contains an integer T (about 2 × 104), indicating the number of test cases. For each test case:

The first and only line contains two integers n and m (3 ≤ n ≤ 105, 

), indicating the number of vertices and the number of edges in the graph.

It's guaranteed that the sum of n in all test cases will not exceed 3 × 107.

output

For each test case output one line containing one integer, indicating the number of different sub-cycle graphs with n vertices and m edges modulo 109 + 7.

题目大意

定义一个简单子环图为:一个 n 个点, m 条边的简单图,可以通过添加若干非负整数条边,使得整个图变成一个 n个点的简单环图。

给定 n 和 m ,表示一个 n 个点, m 条边的简单图,问一共有多少种 n个点 m 条边的简单子环图。
 

思路

sub-cycle graph   可以理解为每个连通块都是一个点或者一条链(显然环也是可以的)。

可以对一下情况进行一些讨论:

1、m>n,边数大于点数,显然无解。

2、m=n,此时图是一整个环,显然图的种类是\frac{(n-1)!}{2}

可以以任意一个点为起始点,然后接下来的情况就是每个点分别能连n-1,n-2……,1种,

总数是(n-1)!,除以2是因为顺时针逆时针是重复的。

3、m=0,显然方案唯一

4、剩下来的情况,也就是 1\leq m<n 的情况,由于每个连通块中都不能存在环,因此我们建的图需要有 n-m个连通块。

首先枚举联通块存在i条链,相当于存在n-m-i个孤立点,方案数是\binom{n}{n-m-i},然后从剩下的m+i个点中去找2i个点,方案数是\binom{m+i}{2i},然后将这些点两两分组,用隔板法,方案数就是\prod_{k=1}^{i}(2k-1), 然后还需要继续考虑除了链端点以外的节点,将剩下的点全排列并分成i份,方案数是(m-i)!*\binom{m-1}{i-1}

所以总的方案就是\sum_{i=1}^{n-m}\binom{n}{n-m-i}*\binom{m+i}{2i}*\prod_{k=1}^{i}(2k-1)*(m-i)!*\binom{m-1}{i-1}

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5+100;
const int mod = 1e9+7;

int P1[N],P2[N];
int ksm(int a,int b) {
	int ans=1;
	while(b) {
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}
void init(int tot) {
	P1[0]=1;
	for(int i=1; i<=tot; i++)P1[i]=1ll*P1[i-1]*i%mod;
	P2[tot]=ksm(P1[tot],mod-2);
	for(int i=tot-1; i>=0; i--)P2[i]=1ll*P2[i+1]*(i+1)%mod;
}
int  C(int n,int m) {
	return 1ll*P1[n]*P2[m]%mod*P2[n-m]%mod;
}

void solve() {
	int x,y;
	cin>>x>>y;
	if (y==0) {
		cout<<"1\n";
	} else if (y>x) {
		cout<<"0\n";
	} else if (x==y) {
		cout<<P1[x-1]*P2[2]%mod<<'\n';
	} else {
		int ans=0;
		int p=1;
		for(int i=1; i<=x-y; i++) {
			if (y+i<i*2) continue;
			int k=C(y+i,i*2)*p%mod;
			p=p*(i*2+1)%mod;
			k=k*P1[y-i]%mod;
			k=k*C(y-1,i-1)%mod;
			ans=(ans+C(x,x-y-i)*k)%mod;
		}
		cout<<ans<<'\n';
	}
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	init(200000);
	int t=1;
	cin>>t;
	while(t--)solve();
	return 0;
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心刍

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值