首页 > 其他分享 >【XSY3997】方格计数(容斥,dp)

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

时间:2022-10-31 07:35:01浏览次数:49  
标签:XSY3997 int big 容斥 ch 对角线 dp mod

题面

方格计数

题解

拼命容斥即可。

先考虑 \(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
*/

标签:XSY3997,int,big,容斥,ch,对角线,dp,mod
From: https://www.cnblogs.com/ez-lcw/p/16842960.html

相关文章

  • 【XSY3990】Alice 和 Bob 双在玩游戏(博弈,dp,拓扑,背包)
    题面Alice和Bob双在玩游戏题解注意到这里一个人无法操作后,另一个人也不一定无法操作(即不像普通的取石子游戏一样),所以考虑转化一下他们各自的最优策略:双方都想让自己......
  • 【XSY3972】树与图(树形dp,树剖,分治NTT)
    题面树与图题解不难发现本题可以转化成以下题目:给定一个\(n\)个点的有根树,你可以在树上选择\(k\)个点,满足对于任意两个点都不互为祖先关系,且从根到每个叶子的路......
  • wordpress独立网站域名解析教程
    网站想要能够访问的第一步就是,把域名解析到我们的服务器IP,这里以阿里云购买的域名举例登阿里云后台找到所有的域名列表   解析域名点击【解析-添加记录】,记......
  • wordpress外贸跨境电商独立站WooCommerce插件安装教程
    如果想要搭建一个外贸独立站,可以使用wordpress配合WooCommerce插件实现电商功能下载插件这里可以去下面地址下载https://woo.weixiaoduo.com/download安装插件网站后......
  • 【XSY3898】强度(期望dp)
    题面强度题解首先题目的要求肯定要转化。有多种转化方法,例如:(其中\(s_i\)代表以\(i\)为根节点的子树大小)\[\begin{aligned}\Epsilon(w(T))=&\Epsilon\left(\sum_{......
  • 【XSY3896】相似(dp套dp,状压)
    题面相似题解可以发现,\(S\)和\(T\)相似,等价于它们的最长公共子序列长度至少为\(n-k\)。首先考虑如何求出两个字符串的\(\text{LCS}\)(最长公共子序列)。考虑dp:......
  • 【XSY3892】【hihocoder1147】时空阵(分层图dp)
    设\(dp(i,t,l)\)表示已经定好前\(i\)层,共有\(t\)个节点,其中第\(i\)层有\(l\)个节点。直接转移即可,注意一些细节:第\(1\)层只有\(1\)号节点。同层之间......
  • wordpress编辑器增加粘贴图片上传服务器教程
    默认的编辑器没有粘贴上传图片功能,现在我们来增加一下安装插件网站后台,找到安装插件界面【插件-安装插件-搜索】 ThePaste  测试插件发布文章的时候,直接使用qq......
  • 【XSY3804】QQ数(莫比乌斯函数,容斥)
    题面题解明显地,这个QQ数可以用\(\mu\)表示,于是询问就变成了这样:\[\begin{aligned}&\sum_{i=1}^n\sum_{d|i}\left(1-\mu(d)^2\right)\\=&\sum_{d=1}^n\left\lfloo......
  • 【XSY3633】匹配(树形 DP,树链剖分,分治)
    考虑最普通的DP:设\(f_{u,i,0/1}\)表示\(u\)子树内恰好包含\(i\)条边的最大权匹配,其中\(u\)有无被匹配。这是个树上背包,暴力DP是\(O(n^2)\)的。可以发现\(f......