【XSY3997】方格计数(容斥,dp)

题面

方格计数

题解

拼命容斥即可。

先考虑 \(k=0\) 的情况。

首先先对对角线的限制容斥,即用 “没有限制-正对角线没选-反对角线没选+正反对角线都没选”。设 \(Z\) 中对角线不能选,设 \(Z\) 中对角线上的格子组成的集合为 \(P\)

然后对行、列也用类似的方式容斥,设 \(X\) 中的行和 \(Y\) 中的列不能选,那么答案即为:

\[\sum(-1)^{|X|+|Y|+|Z|}\binom{\big|\{(i,j)|i\not\in X,j\not\in Y,(i,j)\not\in P\}\big|}{c} \]

注意到 \(\big|\{(i,j)|i\not\in X,j\not\in Y,(i,j)\not\in P\}\big|\) 仅与 \(|X|,|Y|\) 和不在 \(X,Y\) 所包含的行列中却在 \(P\) 中的元素个数有关,不妨设不在 \(X,Y\) 中却在 \(P\) 中的元素个数为 \(t\),那么易得:

\[\big|\{(i,j)|i\not\in X,j\not\in Y,(i,j)\not\in P\}\big|=(n-|X|)(m-|Y|)-t \]

所以我们考虑用 dp 来简化问题,而不是 \(2^{2n}\) 枚举行列的容斥:设 \(f_{i,x,y,t}\) 表示在前 \(i\) 行后 \(i\) 行前 \(i\) 列后 \(i\) 列中(即下图中的阴影部分),有 \(x\) 行不能选,有 \(y\) 列不能选,不在不能选的行和列中却在 \(P\) 中的格子数为 \(t\) 的方案数。

在这里插入图片描述

如图,当我们从 \(f_{i,x,y,t}\)\(f_{i+1}\) 转移时,我们可以枚举第 \(i+1\) 行、第 \(n-i\) 行、 第 \(i+1\) 列、第 \(n-i\) 列选不选,然后计算出相应的 \(x',y',t'\),从 \(f_{i,x,y,t}\)\(f_{i+1,x',y',t'}\) 转移即可。

时间复杂度 \(O(n^4)\)。(省略了许多常数)

\(k>0\) 时,我们同样容斥,钦定某若干个格子必须选,那么它们所在的行和列都必须选,容斥时判断一下即可。

代码如下:

#include<bits/stdc++.h>
 
#define N 35
 
using namespace std;
 
namespace modular
{
    const int mod=10007;
    inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
    inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
    inline int mul(int x,int y){return 1ll*x*y%mod;}
    inline void Add(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
    inline void Dec(int &x,int y){x=x-y<0?x-y+mod:x-y;}
    inline void Mul(int &x,int y){x=1ll*x*y%mod;}
}using namespace modular;
 
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^'0');
        ch=getchar();
    }
    return x*f;
}
 
struct Point
{
    int x,y;
}p[10];
 
int n1[130],C[N*N][N*N];
int n,k,c;
int dp[2][N][N][N<<1];
bool cx[N],cy[N];
 
void init()
{
    for(int i=1;i<128;i++) n1[i]=n1[i>>1]+(i&1);
    C[0][0]=1;
    for(int i=1;i<=n*n;i++)
    {
        C[i][0]=1;
        for(int j=1;j<=i;j++)
            C[i][j]=add(C[i-1][j-1],C[i-1][j]);
    }
}
 
int work2(bool bd0,bool bd1,int cnum)
{
    int nbd=bd0+bd1;
    int now=0;
    memset(dp[now],0,sizeof(dp[now]));
    dp[now][0][0][0]=1;
    for(int i=1,j=n;i<j;i++,j--)
    {
        int nxt=now^1;
        memset(dp[nxt],0,sizeof(dp[nxt]));
        for(int brow=0;brow<=(i-1)*2;brow++)
        {
            for(int bline=0;bline<=(i-1)*2;bline++)
            {
                for(int bd=0;bd<=(i-1)*2*nbd;bd++)
                {
                    if(!dp[now][brow][bline][bd]) continue;
                    for(int u=0;u<2;u++)
                    {
                        if(u&&cx[i]) continue;
                        for(int d=0;d<2;d++)
                        {
                            if(d&&cx[j]) continue;
                            for(int l=0;l<2;l++)
                            {
                                if(l&&cy[i]) continue;
                                for(int r=0;r<2;r++)
                                {
                                    if(r&&cy[j]) continue;
                                    int tmp=0;
                                    if(bd0) tmp+=((!u)&&(!l))+((!d)&&(!r));
                                    if(bd1) tmp+=((!u)&&(!r))+((!d)&&(!l));
                                    Add(dp[nxt][brow+u+d][bline+l+r][bd+tmp],dp[now][brow][bline][bd]);
                                }
                            }
                        }
                    }
                }
            }
        }
        now=nxt;
    }
    if(n&1)
    {
        int i=(n+1)/2;
        int nxt=now^1;
        memset(dp[nxt],0,sizeof(dp[nxt]));
        for(int brow=0;brow<=n-1;brow++)
        {
            for(int bline=0;bline<=n-1;bline++)
            {
                for(int bd=0;bd<=(n-1)*nbd;bd++)
                {
                    if(!dp[now][brow][bline][bd]) continue;
                    for(int u=0;u<2;u++)
                    {
                        if(u&&cx[i]) continue;
                        for(int l=0;l<2;l++)
                        {
                            if(l&&cy[i]) continue;
                            int tmp=((bd0||bd1)&&((!u)&&(!l)));
                            Add(dp[nxt][brow+u][bline+l][bd+tmp],dp[now][brow][bline][bd]);
                        }
                    }
                }
            }
        }
        now=nxt;
    }
    int ans=0;
    for(int brow=0;brow<=n;brow++)
    {
        for(int bline=0;bline<=n;bline++)
        {
            for(int bd=0;bd<=n*nbd-((n&1)&&(nbd==2));bd++)
            {
                if(!dp[now][brow][bline][bd]) continue;
                int able=(n-brow)*(n-bline)-bd;
                if(able<c) continue;
                int tmp=mul(C[able-cnum][c-cnum],dp[now][brow][bline][bd]);
                if((brow+bline)&1) Dec(ans,tmp);
                else Add(ans,tmp);
            }
        }
    }
    return ans;
}
 
int work1(int csta)
{
    memset(cx,0,sizeof(cx));
    memset(cy,0,sizeof(cy));
    bool cd0=0,cd1=0;
    for(int i=1;i<=k;i++)
    {
        if((csta>>(i-1))&1)
        {
            cx[p[i].x]=cy[p[i].y]=1;
            if(p[i].x==p[i].y) cd0=1;
            if(p[i].x+p[i].y==n+1) cd1=1;
        }
    }
    int ans=0;
    for(int bd0=0;bd0<2;bd0++)
    {
        if(bd0&&cd0) continue;
        for(int bd1=0;bd1<2;bd1++)
        {
            if(bd1&&cd1) continue;
            if((bd0+bd1)&1) Dec(ans,work2(bd0,bd1,n1[csta]));
            else Add(ans,work2(bd0,bd1,n1[csta]));
        }
    }
    return ans;
}
 
int main()
{
    n=read(),k=read(),c=read();
    init();
    for(int i=1;i<=k;i++) p[i].x=read(),p[i].y=read();
    int ans=0;
    for(int csta=0;csta<(1<<k);csta++)
    {
        if(n1[csta]&1) Dec(ans,work1(csta));
        else Add(ans,work1(csta));
    }
    printf("%d\n",ans);
    return 0;
}
/*
3 1 4
1 2
*/
/*
16 2 18
1 3
5 7
*/
posted @ 2022-10-31 07:23  ez_lcw  阅读(30)  评论(0编辑  收藏  举报