1. 程式人生 > >牛客國慶集訓派對Day3___H Travel——連通+組合數

牛客國慶集訓派對Day3___H Travel——連通+組合數

題目大意:

    題目較短,但第一次理解起來可能有點繞腦。最後要求的是不同的旅遊方案數,也就是不同路線選取的方案數。

解題思路:

    一開始以為是圖論,想了一下好像無從下口,看了一下題解原來是有規律可循,當m==1m==1時答案為11,當m>1m>1時就可以用m1m-1條邊將整個圖(連通的)分成mm個區域,恰好對應題目中的mm次旅遊,也就是說每次旅遊選取這mm個區域裡的一個,那麼很顯然結果我們就要乘以這個mm的全排列,即旅遊的順序不同。     然後考慮的是怎麼選取這m1m-1條邊,就是從n1n-1條邊中任意選取,也就是C

n1m1C(n-1,m-1),求個組合數。     最後答案即為 Cn1m1C(n-1,m-1)* mm!

程式碼思路:

    看到資料範圍,modmod1e9+71e9+7,用逆元求組合數即可。

核心:對題目的理解,算得上是一道讀題題吧,前提是會一點數論的知識。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1 << 20;
const int mod = 1000000007;
int t, n, m, ta, tb;
ll c[maxn];
ll PowerMod(ll a, ll b, ll c) {	//快速冪 
	ll ans = 1;
	a = a % c;
	while (b>0) {
		if (b % 2 == 1)
			ans = (ans * a) % c;
		b = b / 2;
		a = (a * a) % c;
	}
	return ans;
}
void init() { //先打出階乘,節省時間
	c[1] = 1;
	for (int i = 2; i <= maxn; i++) {
		c[i] = c[i - 1] * i%mod;
	}
}
ll C(ll n, ll m) {	//逆元求組合數 
	return c[n] * PowerMod(c[m] * c[n - m] % mod, mod - 2, mod) % mod;
}
int main(void) {
	scanf("%d", &t);
	init();
	while (t-- >0 ) {
		scanf("%d%d", &n, &m);
		for (int i = 0; i < n - 1; i++)
			scanf("%d%d",&ta,&tb);
		if (m == 1) {
			printf("1\n");
			continue;
		}
		ll ans = C(n - 1, m - 1);
		ans = (ans*c[m]) % mod;
		printf("%lld\n", ans);
	}
	return 0;
}