P10681 [COTS 2024] 奇偶矩阵 Tablica 题解

题面

首先枚举行与列和为 \(1\)\(2\) 的个数,我们设有 \(a\) 行和为 \(1\)\(b\) 行和为 \(2\)\(c\) 列和为 \(1\)\(d\) 列和为 \(2\)。显然它们满足下列关系:

\[\begin{aligned} \begin{equation} \begin{cases} a+b=n\\ c+d=m\\ a+2b=c+2d\\ \end{cases} \end{equation} \end{aligned} \]

所以只要有一个量确定了,其他三个也就定下来了。现在考虑对于固定的 \(a\)\(b\)\(c\)\(d\),我们可以将问题转化一下。这个问题相当于我们现在有 \(m\) 种颜色的小球,其中 \(c\) 种颜色有一个,其余 \(d\) 种有两个,我们现在需要把它们放入 \(n\) 个有标号集合中,其中有 \(a\) 个集合放一个球,其余 \(b\) 个集合放两个球,同一集合不能放入两个同色球。

接下来我们考虑把这 \(m\) 个小球按不同方式排列,对于每种方式我们都钦定前 \(a\) 个球每个球单独放入一个集合中,后面的我们将每相邻两个球放入同一个集合,但是这样可能会将两个同色球放入同一集合,所以我们对小球构成的序列还需要有限制,即后面 \(2b\) 的位置上每相邻两球颜色不同,也就是有 \(b\) 对小球颜色不能相同。这个限制很难描述,所以我们考虑容斥容斥掉了集合内出现同色球的方案。现在这 \(b\) 个集合每个集合交换两元素的位置其实一种方案但被统计了 \(2\) 次,所以每种方案都被重复统计了 \(2^b\) 次,最后还要乘 \(2^{-b}\)

最后整理一下上面的每一步,就可以得到式子:

\[\begin{aligned} ans=\sum\limits_{a+b=n,c+d=m,a+2b=c+2d\\}{n\choose a}{m\choose c}\frac{\sum_{t=0}^{min(b,d)}(-1)^t{b\choose t}{d\choose t}t!\frac{(c+2d-2t)!}{2^{d-t}}}{2^b} \end{aligned} \]

这个式子看着很长,但其实比较简单,建议自己先理解一下。拆开来讲的话就是,在 \(n\) 行中选 \(a\) 行和为 \(1\),在 \(m\) 列中选 \(c\) 列和为 \(1\)。后面要满足 \(b\)
组连续两个小球不同色,我们枚举有 \(t\) 组是同色的,再在 \(b\) 组里选 \(t\) 组,\(d\) 种颜色里选 \(t\) 种,这 \(t\) 组的顺序可以随意交换,其他 \(c+2d-2t\) 个小球位置任意,最后再乘上 \(2^{-b}\) 就可以了。

code:

代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7,N=3e3+5;
ll fac[N<<1],invq[N<<1],n,m,ans,C[N][N];
ll qp(ll x,ll y)
{
	ll res=1;
	while(y)
	{
		if(y&1) (res*=x)%=mod;
		(x*=x)%=mod,y>>=1;
	}
	return res;
}
inline ll rd()
{
    char c;int f=1;
    while(!isdigit(c=getchar()))if(c=='-') f=-1;
    ll x=(c^48);
    while(isdigit(c=getchar()))x=x*10+(c^48);
    return x*f;
}
inline ll h(int i){return (i&1)?(mod-1):1;}
inline void add(ll &x,ll y){x+=y;if(x>=mod) x-=mod;}
inline ll solve(int a,int b,int c,int d)
{
	ll res=0;
	for(int t=0;t<=d&&t<=b;t++) add(res,h(t)*C[b][t]%mod*C[d][t]%mod*fac[t]%mod*fac[c+2*d-2*t]%mod*invq[d+b-t]%mod);
	return res;
}
int main()
{
	n=rd(),m=rd(),fac[0]=invq[0]=1;ll s=2*max(n,m),i2=qp(2,mod-2);
	for(int i=1;i<=s;i++) fac[i]=fac[i-1]*i%mod,invq[i]=invq[i-1]*i2%mod;
	for(int i=0;i<=max(n,m);i++)
	{
		C[i][0]=C[i][i]=1;
		for(int j=1;j<i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
	}
	for(int b=0;b<=n;b++)
	{
		int a=n-b,cnt=b*2+a;
		if(cnt>=m&&cnt<=2*m) add(ans,C[n][b]*C[m][cnt-m]%mod*solve(a,b,2*m-cnt,cnt-m)%mod);
	}
	cout<<ans;
	return 0;
}

posted @ 2025-04-16 16:19  Re_Star  阅读(21)  评论(0)    收藏  举报