1. 程式人生 > >【組合數學&&尤拉降冪】ACM-ICPC 2018 焦作賽區網路預賽 G. Give Candies

【組合數學&&尤拉降冪】ACM-ICPC 2018 焦作賽區網路預賽 G. Give Candies

Step1 Problem:

有 n 個人編號為 1, 2, 3, …, n,有 n 個糖果,按編號從小到大順序分發給它們,所到編號至少得獲得一個糖果,求有多少種分配方案。
資料範圍:
1<=T<=100, 1<=n<=10^100000.

Step2 Ideas:

n個糖果恰好分給 1 個小朋友的方案數:C(n-1, 0)
n個糖果恰好分給 2 個小朋友的方案數:C(n-1, 1)
n個糖果恰好分給 3 個小朋友的方案數:C(n-1, 2)
… … … … … … … … … … … … … … … … … …
… … … … … … … … … … … … … … … … … …
n個糖果恰好分給 n 個小朋友的方案數:C(n-1, n-1)
C(n-1, 0) + C(n-1, 1) + C(n-1, 2) + … + C(n-1, n-1) = 2^(n-1)

對於 n 個糖果恰好分給 i 個小朋友的方案數:等價於 n-i 個球是無標誌的,i 個盒子是有區別的,取 n-1 個球放進盒子,每個盒子允許多於一個球的方案數。
C(i+(n-i)-1, n-i) = C(n-1, n-i) = C(n-1, i-1).

由於 n 很大,我們需要尤拉降冪:A^B mod D = A^( B%phi(D) + phi(D) ) mod D.

Step3 Code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e6+5;
char s[N];
ll get_phi(ll x)
{
    ll ret = x;
    ll m = sqrt(x);
    if(m*m > x) m--;
    for(int i = 2; i <= m; i++)
    {
        if(x%i == 0) {
            ret = ret/i*(i-1);
            while(x%i == 0) {
                x /= i;
            }
        }
    }
    if(x != 1) ret = ret/x*(x-1);
    return ret;
}
ll Pow(ll x, ll n, ll MOD)
{
    ll ans = 1;
    while(n)
    {
        if(n&1) ans *= x, ans %= MOD;
        x = x*x, x %= MOD;
        n >>= 1;
    }
    return ans;
}
int main()
{
    ios::sync_with_stdio(false);
    int T;
    //scanf("%d", &T);
    cin >> T;
    ll MOD;
    while(T--)
    {
        cin >> s;
        MOD = 1e9+7;
        ll phi = get_phi(MOD);
        int len = strlen(s);
        ll ans = 0;
        for(int i = 0; i < len; i++)
        {
            ans = ans*10+s[i]-'0';
            ans %= phi;
        }
        cout << Pow(2, ans+phi, MOD)*Pow(2, MOD-2, MOD)%MOD << endl;
        //printf("%lld\n", Pow(2, ans+phi, MOD)*Pow(2, MOD-2, MOD)%MOD);
    }
    return 0;
}