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

2018 Multi-University Training Contest 2

大致题意:有一个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;
}

猜你喜欢

转载自blog.csdn.net/u013534123/article/details/81236930
今日推荐