6464. 【GDOI2020模拟02.07】矩阵

题目

有个 n ∗ m n*m nm的黑白的方格,根据这个矩阵求得 A 、 B 、 C A、B、C ABC三个数组
A i A_i Ai表示第 i i i行的第一个黑格的位置(如果没有就 m + 1 m+1 m+1)。
B i B_i Bi表示第 i i i列的第一个黑格的位置(如果没有就 n + 1 n+1 n+1)。
C i C_i Ci表示第 j j j列的最后一个黑格的位置(如果没有就 0 0 0)。
求对于所有的涂色方案,不同的三元组 ( A , B , C ) (A,B,C) (A,B,C)的数量。
n ≤ 8000 n\leq 8000 n8000
m ≤ 200 m\leq 200 m200


思考历程

看到这题后没有什么特别的思路。
分析了一些粗浅的性质,对正解(甚至拿部分分)没有什么帮助。
最终就是打了个暴力。


正解

f i , j f_{i,j} fi,j表示前 j j j列中,强制 i i i行有黑格,所得到的方案数。
考虑从 f i , j f_{i,j} fi,j转移到 f i + k , j + 1 f_{i+k,j+1} fi+k,j+1
k = 0 k=0 k=0
由于这 i i i列都有黑格,所以在新增一列时,无论第 j + 1 j+1 j+1列怎么涂, A A A都不会再被更新。
所以只需要考虑 B B B C C C的影响。
很显然影响是 1 + i + C i 2 1+i+C_{i}^{2} 1+i+Ci2
k > 0 k>0 k>0
为了避免重复,新增的 k k k行填的黑格子都在 j + 1 j+1 j+1列上。
原来的行的 A i A_i Ai不变,新增的行的 A i = j + 1 A_i=j+1 Ai=j+1,所以对于 A A A来说,方案数的不同在于新增 k k k列的位置。将 k k k列插入 i i i列中,得出的不同的 A A A乘上 C i + k k C_{i+k}^k Ci+kk种方案。
但是 B B B C C C就可能要难搞一些,因为原来的行在 j + 1 j+1 j+1列上有可能涂黑。
换个角度想一想,把 A A A B B B C C C综合起来考虑。
分类讨论一下:
一、假设 B j + 1 B_{j+1} Bj+1 C j + 1 C_{j+1} Cj+1都是新加的 k k k行之一。
每一种新增 k k k行插入原来的 i i i行的方案,都会对应着有且仅有一个 B j + 1 B_{j+1} Bj+1 C j + 1 C_{j+1} Cj+1的取值。
考虑上 A A A的影响,每个方案的 A A A都是不一样的,所以在这个情况下,每种插入的方案都对应着唯一的 ( A , B , C ) (A,B,C) (A,B,C)
方案数为 C i + k k C_{i+k}^k Ci+kk
二(三)、假设 B j + 1 B_{j+1} Bj+1是原来的 i i i行之一, C j + 1 C_{j+1} Cj+1是新加的 k k k行之一(反之同理):
可以这么理解:先把新增的 k k k行插入原来的 i i i行中,这时候对应着唯一的 A A A,所以不用担心后面可能出现重复的情况。
插的第一个位置前面的行中任选一个作为 B j + 1 B_{j+1} Bj+1
硬算的话,就先枚举第一个插入后的位置 x + 1 x+1 x+1(表示这个位置前面有 x x x个原来的行),乘上 x x x的贡献,然后将 k − 1 k-1 k1行插入后面的 i + k − x − 1 i+k-x-1 i+kx1行中的贡献乘进去。列出个丑陋的式子,就是它的方案数。
通过组合数的定义来分析,这无非就是:在 i + k i+k i+k个东西中,在位置 x + 1 x+1 x+1处选一个,然后它左边选 1 1 1个,右边选 k − 1 k-1 k1个。
这个东西不就是 i + k i+k i+k个中选 k + 1 k+1 k+1个嘛!
方案数出来了: C i + k k + 1 C_{i+k}^{k+1} Ci+kk+1
四、假设 B j + 1 B_{j+1} Bj+1 C j + 1 C_{j+1} Cj+1都是原来的 i i i行之一
类似二的分析,得出方案数为 C i + k k + 2 C_{i+k}^{k+2} Ci+kk+2
综合起来,贡献总共就是 C i + k + 2 k + 2 C_{i+k+2}^{k+2} Ci+k+2k+2
把式子列出来,显然可以卷积。
NTT就完了。


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define L 16384
#define N 8010
#define M 210 
#define ll long long
#define mo 998244353
ll qpow(ll x,int y=mo-2){
	ll res=1;
	for (;y;y>>=1,x=x*x%mo)
		if (y&1)
			res=res*x%mo;
	return res;
}
int n,m;
ll fac[N],ifac[N];
ll f[N];
ll a[L],b[L],c[L];
int nN,lgn,re[L];
ll A[L],B[L],C[L];
inline void dft(ll A[],int flag){
	for (int i=0;i<nN;++i)
		if (i<re[i])
			swap(A[i],A[re[i]]);
	for (int i=1;i<nN;i<<=1){
		ll wn=qpow(3,(mo-1)/(2*i));
		if (flag==-1)
			wn=qpow(wn);
		for (int j=0;j<nN;j+=i<<1){
			ll wnk=1;
			for (int k=j;k<j+i;++k,wnk=wnk*wn%mo){
				ll x=A[k],y=wnk*A[k+i]%mo;
				A[k]=(x+y)%mo;
				A[k+i]=(x-y+mo)%mo;
			}
		}
	}
	if (flag==-1){
		ll inv=qpow(nN);
		for (int i=0;i<nN;++i)
			A[i]=A[i]*inv%mo;
	}
}
inline void multi(ll c[],ll a[],ll b[]){
	for (lgn=0,nN=1;nN<=2*n;nN<<=1,lgn++);
	re[0]=0;
	for (int i=1;i<nN;++i)
		re[i]=re[i>>1]>>1|(i&1)<<lgn-1;
	for (int i=0;i<nN;++i)
		A[i]=a[i],B[i]=b[i];
	dft(A,1),dft(B,1);
	for (int i=0;i<nN;++i)
		C[i]=A[i]*B[i]%mo;
	dft(C,-1);
	for (int i=0;i<nN;++i)
		c[i]=C[i];
}
int main(){
	freopen("matrix.in","r",stdin);
	freopen("matrix.out","w",stdout);
	scanf("%d%d",&n,&m);
	int most=max(n,m)+2;
	fac[0]=1;
	for (int i=1;i<=most;++i)
		fac[i]=fac[i-1]*i%mo; 
	ifac[most]=qpow(fac[most]);
	for (int i=most-1;i>=0;--i)
		ifac[i]=ifac[i+1]*(i+1)%mo;
	f[0]=1;
	for (int j=1;j<=m;++j){
		for (int i=0;i<=n;++i)
			a[i]=f[i]*ifac[i]%mo;
		b[0]=0;
		for (int k=1;k<=n;++k)
			b[k]=ifac[k+2];
		multi(c,a,b);
		for (ll i=0;i<=n;++i)
			f[i]=(c[i]*fac[i+2]+f[i]*(1+i+(i*(i-1)>>1)%mo))%mo;
	}
	ll ans=0;
	for (int i=0;i<=n;++i)
		ans+=f[i]*fac[n]%mo*ifac[i]%mo*ifac[n-i]%mo;
	printf("%lld\n",ans%mo);
	return 0;
}

总结

计数类的问题中,如果式子太复杂,就用定义的角度去分析它吧……

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值