HDU 6314(容斥原理)

Matrix

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


 

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

 容斥原理。

假设不考虑列,只看行,我们令行容斥系数为fa(j)

那么

ans=\sum_{j=a}^{n}\left ( fa[j]*C_{n}^{j}*2^{n-j} \right )

意思就是,我枚举至少染了j行黑色的答案,那么显然是C_{n}^{j}*2^{n-j},通过容斥系数,我得到的是刚刚好染了j行黑色的答案

那么接下来的问题就变成了,怎么求这个容斥系数

可以这么考虑,每个刚刚好染了j行黑色的答案在总答案中只能出现一次,那么,我只要统计前面的情况中,这个答案出现了多少次,假如为sum,那么,我的fa(j)=1-sum即可

对于一个<j的数k,考虑j的答案在k的出现次数,显然,为C_{j}^{k}*fa[k]。我们可以这么想,在j的答案中,一定包含了“至少k行被染色”的答案,那么,这个数目相当于是我在确定染色的j行中选择k行,然后在乘上容斥系数,就是在k的答案中j的答案个数。

那么,我们最后可以得到这样一个等式:fa[j]=1-\sum_{k=a}^{j-1}(C_{j}^{k}*fa[k])

对于列的情况,其实是类似的。

最后我们只要nm枚举(i,j),计算一下对应项的乘积就可以出结果了。

 

一切看起来都是这么的顺利,一气呵成。

然而。。。。。。

妈耶,这么优秀的做法也能T成智障????

通过不断的降低取模次数(比如不对负数取模变回正数,而是最后答案如果为负数的时候变为正数),以及良好的C语言编码风格(没错就是在外面定义好循环的变量i,j,k),身残志坚的老年人终于

卡着时间过去。。。。。。

小小的吐槽一下,这个1s的时限真的大丈夫吗- =

#include <bits/stdc++.h>
#define fir first
#define se second
#define mp make_pair
#define pb push_back
#define ll long long
using namespace std;
const int maxn=3000+10;
const ll mod=998244353;
const double eps=1e-7;
const int maxm=1e6+10;
const int inf=0x3f3f3f3f;
//怕炸空间没敢开long long 
int c[maxn][maxn];   //组合数
int p[maxn*maxn];   //2的倍数
int g[maxn][maxn];
int n,m,a,b;
int i,j,k;
void init(){
    for (i=0;i<maxn;i++) c[i][0]=1;
    for (i=1;i<=3000;i++){
        for (j=1;j<=i;j++){
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
        }
    }
    p[0]=1;
    for (i=1;i<maxn*maxn;i++)
        p[i]=(ll)p[i-1]*2%mod;
}
int fa[maxn],fb[maxn];  //容斥系数
int main(){
    init();
    while (scanf("%d %d %d %d",&n,&m,&a,&b)!=EOF){
        ll ans=0;
        for (j=a;j<=n;j++){
            fa[j]=1;
            for (k=a;k<=j-1;k++){
                fa[j]=(fa[j]-(ll)c[j][k]*fa[k])%mod;
                //这个部分在前面出现了c[j][k]*fa[k]次
                //考虑j之前的结果,在计算的时候包括了j的情况,相当于在j行中选出k行作为a[k]的计算结果
                //因为容斥的时候还有a[k]的系数,所以要乘上一个a[k]
               //fa[j]=(fa[j]+mod)%mod;
            }
        }
        for (j=b;j<=m;j++){
            fb[j]=1;
            for (k=b;k<=j-1;k++){
                fb[j]=(fb[j]-(ll)c[j][k]*fb[k])%mod;
                //这个部分在前面出现了c[j][k]*fa[k]次
                //考虑j之前的结果,在计算的时候包括了j的情况,相当于在j行中选出k行作为a[k]的计算结果
                //因为容斥的时候还有a[k]的系数,所以要乘上一个a[k]
                //fb[j]=(fb[j]+mod)%mod;
            }
        }
        /*for (int i=1;i<=n;i++)
            cout<<fa[i]<<" ";
        cout<<endl;
        for (int i=1;i<=m;i++)
            cout<<fb[i]<<" ";
        cout<<endl;*/
        for (i=a;i<=n;i++){
            for (j=b;j<=m;j++){
                g[i][j]=(ll)p[(n-i)*(m-j)]*c[n][i]%mod;
                g[i][j]=(ll)g[i][j]*c[m][j]%mod;
            }
        }
        for (i=a;i<=n;i++){
            for (j=b;j<=m;j++){
                ans=(ans+(ll)fa[i]*fb[j]%mod*(ll)g[i][j])%mod;
            }
        }
        if (ans<0){
            ans=(ans+mod)%mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值