好题推荐 CF1931G One-Dimensional Puzzle

题意

有如下几种拼图(具体形状见题面):

  1. 十字型(左凸右凸)
  2. 工字型(左凹右凹)
  3. 左凹右凸
  4. 左凸右凹

给你每种拼图的个数,问你将他们凸对凹拼在一起有多少种方案,答案对 998244353 998244353 998244353 取余。

题解

Part.1 前置知识

Part.1-1 隔板法

给你 n n n 个苹果,要求你将这些 相同 的苹果分给 m m m不同 的小朋友,每个小朋友都有至少一个苹果,问你分发苹果的方案数。

我们可以先作图分析一下:

  • 首先,我们画出 n n n 个苹果。
  • 然后,我们随意在 两个苹果之间 画出 m − 1 m-1 m1 个隔板。
  • 接下来,我们将被隔板分成的 m m m 个区间里的苹果依次给这些小朋友。

隔板法
可以发现,这时的方案数等于 C n − 1 m − 1 C_{n-1}^{m-1} Cn1m1

若有的小朋友可以没有苹果,那么可以赠送每个小朋友一个苹果,那么此时的方案数等于 C n + m − 1 m − 1 C_{n+m-1}^{m-1} Cn+m1m1

这时,若选出的苹果个数序列为 { 1 , 1 , 2 } \{1,1,2\} {1,1,2},那么每个小朋友实际分到的个数序列是 { 0 , 0 , 1 } \{0,0,1\} {0,0,1}

Part.1-2 组合数

上一部分,我们介绍了隔板法。可以发现,我们用到了组合数,这里给出组合数的两种求法。

Part.1-2-1 杨辉三角

杨辉三角给出了组合数的递推式。

C n m = C n − 1 m + C n − 1 m − 1    ( 0 < m < n ) C_{n}^{m}=C_{n-1}^{m}+C_{n-1}^{m-1}~~(0<m<n) Cnm=Cn1m+Cn1m1  (0<m<n)

C n 0 = C n n = 1 C_{n}^{0}=C_{n}^{n}=1 Cn0=Cnn=1

很像动态规划呀!

仔细看,可以发现每个组合数递推下来只有两种情况:选或不选。

C n − 1 m C_{n-1}^{m} Cn1m 表示不选, C n − 1 m − 1 C_{n-1}^{m-1} Cn1m1 表示选。

Part.1-2-2 逆元

可以从组合数的手动求解开始:

C n m = n × ( n − 1 ) × ( n − 2 ) × ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ × ( n − m + 1 ) m × ( m − 1 ) × ( m − 2 ) × ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ × 1 C_{n}^{m}=\frac{n\times(n-1)\times(n-2)\times······\times(n-m+1)}{m\times(m-1)\times(m-2)\times······\times1} Cnm=m×(m1)×(m2)×⋅⋅⋅⋅⋅⋅×1n×(n1)×(n2)×⋅⋅⋅⋅⋅⋅×(nm+1)

C n m = n ! m ! × ( n − m ) ! C_{n}^{m}=\frac{n!}{m!\times(n-m)!} Cnm=m!×(nm)!n!

但是,我们虽然脱离了组合数,但是无法处理除法取余,那就用到逆元了。

逆元所需的知识如下:

Part.1-2-2-1 费马小定理

费马小定理求逆元-百度

最终的结论是 n p − 2 n^{p-2} np2 n n n 在模 p p p 意义下的逆元。

Part.1-2-2-2 快速幂

前面介绍的费马小定理需要求解幂次问题,而且幂次极大。暴力搜索无法通过,需要使用快速幂:

a 2 p + 1 = ( a p ) 2 × a a^{2p+1}=(a^{p})^{2}\times a a2p+1=(ap)2×a

a 2 p = ( a p ) 2 a^{2p}=(a^{p})^{2} a2p=(ap)2

代码:

int Pow(int a,int n){
	int ans=1;
	while (n){
		if (n&1){
			ans=ans*a%mod;
		}a=a*a%mod;
		n/=2;
	}return ans;
}

Part.2 求解问题

Part.2-1 解的存在性

若十字型和工字型的差超过 1 1 1,那么一定会有两个十(工)字型在一起,他们无法拼接。

注意到,若将左凸右凹插在工字型后面,刚好还是工字型(十字型同理)。

Part.2-2 c 1 = c 2 c_{1}=c_{2} c1=c2

现在,我们终于要用到前缀知识了!

现在共有两种方案:

  1. 工-十-工-十
  2. 十-工-十-工

现在要把 3 3 3 号和 4 4 4 号拼图插入其中。

Part.2-2-1 工十工

这时可以发现此时有 c 1 + 1 c_{1}+1 c1+1 个位置可以放置 3 3 3 号, c 1 c_{1} c1 个位置放 4 4 4 号。

方案数为 C c 1 + c 3 c 3 − 1 × C c 1 + c 4 − 1 c 4 − 1 C_{c_{1}+c_{3}}^{c_{3}-1}\times C_{c_{1}+c_{4}-1}^{c_{4}-1} Cc1+c3c31×Cc1+c41c41

Part.2-2-2 十工十

这时的话可以发现此时有 c 1 c_{1} c1 个位置可以放置 3 3 3 号, c 1 + 1 c_{1}+1 c1+1 个位置放 4 4 4 号。

方案数为 C c 1 + c 3 − 1 c 3 − 1 × C c 1 + c 4 c 4 − 1 C_{c_{1}+c_{3}-1}^{c_{3}-1}\times C_{c_{1}+c_{4}}^{c_{4}-1} Cc1+c31c31×Cc1+c4c41

两种方案相加即可。

Part.2-3 剩下的情况

现在, 3 , 4 3,4 3,4 号都有 m a x ( c 1 , c 2 ) max(c_{1},c_{2}) max(c1,c2) 个位置,记为 k k k;

方案数为 C k + c 3 − 1 c 3 − 1 × C k + c 4 − 1 c 4 − 1 C_{k+c_{3}-1}^{c_{3}-1}\times C_{k+c_{4}-1}^{c_{4}-1} Ck+c31c31×Ck+c41c41

代码有些混乱,敬请谅解。

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define mod 998244353
int fac[2000010];
int Pow(int a,int n){
	int ans=1;
	while (n){
		if (n&1){
			ans=ans*a%mod;
		}a=a*a%mod;
		n/=2;
	}return ans;
}int C(int a,int b){
	return fac[a]*Pow(fac[b],mod-2)%mod*Pow(fac[a-b],mod-2)%mod;
}
signed main(){
	fac[0]=1;
	for (int i=1; i<=2000000; i++){
		fac[i]=fac[i-1]*i%mod;
	} int t; cin>>t;
	while (t--){
		int a,b,c,d;
		cin>>a>>b>>c>>d;
		if (abs(a-b)>1){
			cout<<"0\n";
		}else{
			int x=(a>b?a:a+1),y=(a>b?b+1:b);
			if (a!=b) cout<<C(x+c-1,x-1)*C(y+d-1,y-1)%mod<<"\n";
			else{
				if (a==0){
					if (c==0||d==0){
						cout<<"1\n";
					}else cout<<"0\n";
				}
				else cout<<(C(x+c-1,x-1)*C(y+d-1,y-1)%mod+C(y+c-1,y-1)*C(x+d-1,x-1)%mod)%mod<<"\n";
			}
		}
	}
	return 0;
} 
  • 37
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值