2017 Multi-University Training Contest - 第一場 04 Division Game (NTT+數學)

題目鏈接:
HDU 6036

題解:
(官方:題解)

顯然每個石子堆最多做  m i=1 e i   (記爲 w  )次操作。此外,如果定義一個堆做 x  次操作恰好變爲 1  的方案數爲 f(x)  ,顯然每個數字做少於 x  次操作不變爲 1  的方案數也是 f(x) 

爲了統計結束於石子堆i  的情況數,我們可以枚舉這是它第幾次操作時結束的,不妨設爲x  ,則對應的方案數恰好是 f(x+1) i1 f(x) ki+1   ,因爲編號小於等於 i  的石子堆做了 x  次操作不變爲,其他石子堆做 x1  次操作恰好變爲 1  ,且只有石子堆 i  變成了一個石子。因此我們在 O(wk)  時間複雜度下將問題規約到 f(x)  的計算。

我們可以基於一個數的不同質因子幾乎互不影響的觀察得到第一個結論。每次操作保證任何一個 e i   (e i >0)  可以減少且至少一個 e i   會減少。自然而然我們可以發現一個容斥關係。

考慮f(x)  的組成,不妨設在某種方案中第 j  次操作使 e i   減少了d(i,j)  (非負值)。我們知道對於每個 e i   x j=1 d(i,j)=e i   ,且對於每個j  m i=1 d(i,j)>0  。進一步我們能發現f(x)  也就是分配d(i,j) (1im,1jx)  使其滿足上述兩個條件的方案數。

假設只滿足第一個條件的相應方案數爲g(x)  ,我們可以發現每個i  分別對應一個組合問題,從而是有:g(x)= i=1 m (e i +x1x1) 

我們也可以觀察到如果某些j  與第二個條件產生了矛盾,與之相關的d(i,j)  都會是零。利用容斥原理可以得到 f(x)= y=0 x (1) xy (xy)g(y)  。這個式子可以化爲一個卷積式子 f(x)x! = y=0 x (1) xy (xy)! g(y)y!   。因此你可以用一些方法來加速卷積,不過 985661441=235×2 22 +1  是一個特殊的質數,所以我們推薦使用 NTT 

總時間複雜度爲O(wm+wlogn+wk)  ,然而實現上謹慎一些也是很有必要的。

AC代碼:

#include<bits/stdc++.h> 
using namespace std;
typedef long long ll;
const int maxm = 11;
const int maxn = 100010;
const int maxLen = 18;
const int maxs = 1<<18;
const int mod = 985661441;
const int gen = 3; // MOD的原根,當且僅當 g^(MOD-1) = 1 % MOD  
ll q_mod(ll a,int b)     
{  
    ll ans=1;  
    while(b>0)  
    {  
        if(b&1)     
            ans=(ans*a)%mod;               
        b>>=1;    
        a=(a*a)%mod;  
    }  
    return ans;  
} 
int inv2[maxLen + 1], w[maxs];
int mod_add(int x, int y)
{
    if(x+y>=mod)
    {
        return x+y-mod;
    }
    else return x+y;
}
int mod_sub(int x, int y)
{
    if(x-y<0){
        return x-y+mod;
    }
    else return x-y;
}
void rader(int y[], int len)  
{  
    for(int i = 1, j = len / 2; i < len - 1; i++){  
        if(i < j) swap(y[i], y[j]);  
        int k = len / 2;  
        while(j >= k) {  
            j -= k;  
            k /= 2;  
        }  
        if(j < k) j += k;  
    }  
}  
void NTT(int len, int x[], bool flag)//NTT
{
    rader(x, len); 
    for(int i = 1, d = 1; d < len; i++, d <<= 1)
    {
        for(int j = 0; j < len; j += d << 1)
        {
            for(int k = 0; k < d; ++k)
            {
                int t = 1LL * w[(maxs >> i) * k] * x[j + k + d] % mod;
                x[j + d + k] = mod_sub(x[j + k], t);
                x[j + k] = mod_add(x[j + k], t);
            }
        }

    }

    if(flag)
    {
        reverse(x + 1, x + len);
        int bitLen = 0;
        while(1<<bitLen < len)
        {
            bitLen++;
        }
        int val = inv2[bitLen];
        for(int i = 0; i < len; i++)
        {
            x[i] = 1LL*x[i] * val % mod;
        }

    }
}
int fact[maxn<<1];
int iact[maxn];
int n, m, k, e[maxm];
int h[2][maxm];
int cur, pre = 1;
int ans[maxm];
int f[maxs], g[maxs];

void init()
{
    w[0] = 1;
    w[1] = q_mod(gen, (mod - 1) >> maxLen);
    for(int i = 2; i < maxs; i++)
    {
        w[i] = 1LL*w[i - 1] * w[1] % mod;
    }

    inv2[0] = 1;
    inv2[1] = (mod + 1) >> 1;
    for(int i = 2; i <= maxLen; i++)
    {
        inv2[i] = 1LL*inv2[i - 1] * inv2[1] % mod;
    }

    fact[0] = 1;
    for(int i = 1; i < maxn << 1; i++)
    {
        fact[i] = 1LL*fact[i - 1] * i % mod;
    }

    iact[1] = 1;
    for(int i = 2; i < maxn; i++)
    {
        iact[i] = mod - (int)(mod / i * 1LL*iact[mod % i] % mod);
    }

    iact[0] = 1;
    for(int i = 1; i < maxn; i++)
    {
        iact[i] = 1LL*iact[i - 1] * iact[i] % mod;
    }    
}
int main()
{ 
    init();
    int Case = 0;  
    while(~scanf("%d%d",&m,&k))
    {
        Case++;
        n = 0;
        for(int i = 0; i < m; i++)
        {
            scanf("%*d%d", &e[i]);
            n += e[i];
        }
        int len=1;
        while(len < (n+1)<<1)
        {
            len <<= 1;
        }

        f[0] = 0;
        int delta = 1;
        for(int i = 0; i < m; i++)
        {
            delta = 1LL*delta * iact[e[i]] % mod;
        }

        for(int i = 1; i <= n; i++)
        {
            f[i] = 1LL*delta * iact[i] % mod * q_mod(iact[i - 1], m) % mod;
            for(int j = 0; j < m; j++)
            {
                f[i] = 1LL*f[i] * fact[e[j] + i - 1] % mod;
            }

        }
        memset(f + n + 1, 0, (len - n - 1) * sizeof(int));

        NTT(len, f, 0);
        for(int i = 0; i <= n; i++)
        {
            g[i] = i & 1 ? mod - iact[i] : iact[i];
        }

        memset(g + n + 1, 0, (len - n - 1) * sizeof(int));

        NTT(len, g, 0);
        for(int i = 0; i < len; i++)
        {
             f[i] = 1LL*f[i] * g[i] % mod;
        } 
        NTT(len, f, 1);

        memset(ans + 1, 0, k * sizeof(int));
        for(int i = 1; i <= n; i++)
        {
            cur ^= 1;
            pre ^= 1;
            f[i] = 1LL*f[i] * fact[i] % mod;
            h[cur][0] = 1;
            for(int j = 1; j <= k; j++)
            {
                h[cur][j] = 1LL*h[cur][j - 1] * f[i] % mod;
            }       
            if(i > 1)
            {
                for(int j = 1; j <= k; j++)
                {
                    ans[j] = (ans[j] + (ll)h[cur][j - 1] * h[pre][k - j + 1]) % mod;
                }
            }                    
        }
        if((ans[1] += h[cur][k]) >= mod)
        {
             ans[1] -= mod;
        } 
        printf("Case #%d:", Case);
        for(int i = 1; i <= k; i++){
            printf(" %d", ans[i]);
        }    
        puts("");
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章