1. 程式人生 > >HDU 6314 2018HDU多校賽第二場 Matrix(容斥原理+組合計數)

HDU 6314 2018HDU多校賽第二場 Matrix(容斥原理+組合計數)

Matrix

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 332768/332768 K (Java/Others)
Total Submission(s): 445    Accepted Submission(s): 106

Problem Description

Samwell Tarly is learning to draw a magical matrix to protect himself from the White Walkers.
the magical matrix is a matrix with n rows and m columns, and every single block should be painted either black or white.
Sam wants to know how many ways to paint the matrix, satisfied that the final matrix has at least A rows, B columns was painted completely black. Cause the answer might be too big, you only need to output it modulo 998244353.

Input

There might be multiple test cases, no more than 5. You need to read till the end of input.
For each test case, a line containing four integers n,m,A,B.
1≤n,m,A,B≤3000.

Output

For each test case, output a line containing the answer modulo 998244353.

Sample Input

3 4 1 2

Sample Output

169

Source

大致題意:有一個m*n的矩陣,每一個格子只能夠塗上黑或者白,然後現在要求至少有a行和b列全部塗黑,問有多少種塗色方案。

首先,%%% dls。這個容斥原理還是挺好想的,但是把一個O(n^{4})的求和式子優化到O(n^{2})還是tql……

題目要求是至少有a行和b列全部塗黑,那麼可以考慮最後的答案ans=\sum_{u=a}^{n}\sum_{x=b}^{m}time(u,x),其中time(u,x)表示恰好有u行和x列被塗黑的方案,也意味著有n-u行和m-x列沒被完全塗黑,於是有: \large time(u,x)=C_n^u*C_m^x*f(n-u,m-x),其中這個f(n-u,m-x)表示n-u行和m-x列沒被完全塗黑的方案數。接下來,我們就來討論如何去求這個f(n-u,m-x)。

討論沒被完全圖合,我們可以考慮用總方案數減去有那麼多行和列被完全塗黑的方案數。顯然,這個東西符合二維的容斥原理,可以直接套用容斥原理的容斥係數,根據組合數學知識,可以得到f(n-u,m-x)的公式:

                  \large f(n-u,m-x)=\sum_{v=0}^{n-u}\sum_{y=0}^{m-x}(-1)^{u+v}*C_{n-u}^v*C_{m-x}^y*2^{(n-u-v)*(m-x-y)}

然後,我們把兩個式子整理一下,就可以得到最後的式子:

                  \large ans=\sum_{u=a}^{n}\sum_{x=b}^{m}\sum_{v=0}^{n-u}\sum_{y=0}^{m-x}(-1)^{u+v}*C_{n-u}^v*C_{m-x}^y*2^{(n-u-v)*(m-x-y)}

可以看到,這是一個含有四個sigma的求和式子,直接去求的話肯定T到死……接下來就請dls開始他的表演。首先我們做一些變數替換令z=m-x-y,w=n-u-v,然後把組合數帶入並且整理,可以得到下面的式子:

                  \large ans=\sum_{u=a}^{n}\sum_{x=b}^{m}\sum_{v=0}^{n-u}\sum_{y=0}^{m-x}\frac{(-1)^{v+y}*2^{wz}*n!*m!}{u!*v!*x!*y!*z!*w!}

注意到,這個式子裡有一些相似的東西,可以拆成 \large \frac{(-1)^v}{u!*v!}\large \frac{(-1)^y}{x!*y!}\large \frac{2^{wz}*n!*m!}{w!*z!} 的乘積,然後又有n-w=u+v,m-z=x+y。注意到,這幾項都只與自己的兩個變數有關,所以可以單獨先求和。於是,我們考慮在計算的時候只列舉w和z,然後預處理s(u+v,a)s(x+y,b)表示上面三項的前兩項,wz(w,z)表示最後一項。

\large s(u+v,v)=\sum_{v=a}^{n}\frac{(-1)^v}{u!*v!}                 \large s(x+y,y)=\sum_{y=b}^{m}\frac{(-1)^y}{x!*y!}               \large wz(w,z)=\frac{2^{wz}}{w!*z!}

預處理之後,對於給定的n和m,我們就可以在\large O(n^2)時間複雜度內求出最後答案,答案為:

                 \large ans=\sum_{w=0}^{n-a}\sum_{z=0}^{m-b}s(n-w,a)*s(m-z,b)*wz(w,z)

雖然說最後的理論時間複雜度為\large O(n^2),但是本題時間卡的比較死,常數上需要注意一點,具體寫法見程式碼:

#include<bits/stdc++.h>
#define IO ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define mod 998244353
#define LL long long
#define N 3001

using namespace std;

int m,n,a,b,pw[N*N],wz[N][N],s[N][N],fac[N];
LL inv[N],ans;

void init()
{
    inv[0]=inv[1]=pw[0]=fac[1]=1;
    for(int i=1;i<N*N;i++) 
        pw[i]=pw[i-1]*2%mod;
    for(int i=2;i<N;i++)
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
    for(LL i=2;i<N;i++)
    {
        fac[i]=fac[i-1]*i%mod;
        inv[i]=inv[i-1]*inv[i]%mod;
    }
    for(int i=0;i<N;i++)
    {
        for(int j=i;j>=0;j--)
        {
            int delta=inv[j]*inv[i-j]%mod;
            if ((i-j)&1) delta=mod-delta;
            s[i][j]=s[i][j+1]+delta;
            if (s[i][j]>=mod) s[i][j]-=mod;
        }
        for(int j=0;j<N;j++)
           wz[i][j]=pw[i*j]*inv[i]%mod*inv[j]%mod;
    }
}

int main()
{
    IO; init();
    while(cin>>n>>m>>a>>b)
    {
        ans=0;
        for(int w=0;w<=n-a;w++)
            for(int z=0;z<=m-b;z++)
            {
                ans=ans+1LL*s[n-w][a]*s[m-z][b]%mod*wz[w][z]%mod;
                if (ans>=mod) ans-=mod;
            }
        cout<<ans*fac[n]%mod*fac[m]%mod<<endl;
    }
    return 0;
}