1. 程式人生 > >2018 Wannafly summer camp Day3-- Travel (思維 組合數取模)

2018 Wannafly summer camp Day3-- Travel (思維 組合數取模)

題目大意:

       魔方國有n座城市,編號為。城市之間通過n-1條無向道路連線,形成一個樹形結構。瀾瀾打算在魔方國進行m次旅遊,每次遊覽至少一座城市。為了方便,每次旅遊遊覽的城市必須是連通的。此外,瀾瀾希望遊覽所有城市恰好一次。        瀾瀾想知道有多少種旅遊方案滿足條件,兩個方案不同當且僅當存在某一次旅遊遊覽了不同的城市。瀾瀾不會數數,所以只好讓你來幫他數方案。

題解:

      第一次見到披著樹形結構的外衣卻與樹完全不相干的題。

      首先給了n個點,n-1條邊,那麼肯定是一棵樹。進行m次旅行,每個點都要經過且只能經過一次,那就是用m條邊將n個點分為m段,這裡就是隔板法的思想,劃分數就是C_{n-1}^{m-1}

(將n個點劃分為m段,就是在n-1個空中插入m-1個板)。

-----------------------------------------------------------------------------------------------------------------------------------------------------------------

      在網上也看了一些題解,都是一個版本的,感覺並沒有說的很清楚。我認為本題應該是劃分點而不是劃分邊,即劃分n個點而不是n-1條邊,因為並不一定所有點都在一條邊上。

      比如這個圖,將6個點的樹劃分為3段,那麼左邊那一段是不可能通過一次旅行來走完的。所以劃分邊不行,需要劃分點。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#define mod 1000000007
using namespace std;
typedef long long ll;
#define maxn 1000010
int t, n, m, ta, tb;
ll c[maxn];
long long pow_mod(long long a,long long n,long long m)//a^n mod m
{
    long long res=1;
    while(n>0)
    {
        if(n&1==1)
            res=res*a%m;
        a=a*a%m;
        n>>=1;
    }
    return res;
}
//記得在最後輸出結果的時候再模m一次

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] * pow_mod(c[m] * c[n - m] % mod, mod - 2, mod) % mod;
}
int main()
{
    init();
    int T,x,y;
    cin>>T;
    while(T--)
    {
        cin>>n>>m;
        for(int i = 0; i < n - 1; i++)
            cin>>x>>y;
        if(m == 1)
        {
            puts("1");
            continue;
        }
        ll ans = C(n, m);
        ans = (ans*c[m]) % mod;
        cout << ans << endl;
    }
    return 0;
}