Codeforces-1473-G. Tiles (范德蒙德卷积+快速数论变换NTT)

题目

传送门
在这里插入图片描述

思路

这是题解的思路:传送门
在这里插入图片描述
我就再具体写写我对题解的几点理解吧。

  • 首先,解决这个递推式的问题:

a n s i + 1 , j = ∑ k = 1 m C a + b b − k + j a n s i , k ans_{i+1,j}=\sum^{m}_{k=1}{C^{b-k+j}_{a+b} ans_{i,k}} ansi+1,j=k=1mCa+bbk+jansi,k

  1. 解释一下 i,j,k 的意义吧。 i,j 指现在讨论从第 i 列走到第 i+1 列的顺序第 j 个位置;k 代表枚举从第 i 列的顺序第 k 个位置出发。
    在这里插入图片描述
  2. 那么我们先看从 (i,k)(i+1,j) 的贡献,枚举前 a 次向下的次数 t,就可得出一个这样的贡献:
    ∑ k = 0 j + b − k C a t C b j − k + b − t a n s i , k \sum^{j+b-k}_{k=0} {C^t_a C^{j-k+b-t}_b} ans_{i,k} k=0j+bkCatCbjk+btansi,k
    通过范德蒙德卷积可以得到这个式子实际上等于
    C a + b j − k + b a n s i , k C^{j-k+b}_{a+b} ans_{i,k} Ca+bjk+bansi,k
    那么对 k 求个和就是最上面那个递推式了。
    关于范德蒙德卷积可以参考这个博客 https://blog.csdn.net/qq_42880894/article/details/87814601

有了这个递推式,正如题解所说,对每个 i 算完的时间复杂度是 O(n^2),那么总的时间复杂度应该是 O(n^3),对于 1000 来说可能会超时,于是就得用 NTT 加速了。

  • 接下来就是我对这题 NTT 的理解(现学 QAQ)

代码

初代(未用NTT加速,会TLE)

#include <cstdio>
#define N 1005
#define Ha 998244353
typedef long long LL;
int n,h,a[N],b[N];
LL jc[10005];	//阶乘 
LL ans[N][N<<3];

//快速幂 
LL ksm(LL x, LL t) {
	LL ret=1;
	for (; t; (t>>=1),(x=x*x%Ha)) {
		if (t&1) {
			ret=ret*x%Ha;
		}
	}
	return ret;
}

//组合数 
LL C(LL x, LL y) {
	
	if (y>x || y<0) {
		return 0;
	}
	
	if (x-y>y) {
		y=x-y;
	}
	
	LL ret;
	ret=jc[x];
	ret=ret*ksm(jc[y],Ha-2)%Ha;
	ret=ret*ksm(jc[x-y],Ha-2)%Ha;
	return ret;
}


int main() {
	
	
	jc[0]=jc[1]=1;
	for (int i=2; i<=10000; i++) {
		jc[i]=(jc[i-1]*i)%Ha;
	}
	
	scanf("%d",&n);
	for (int i=1; i<=n; i++) {
		scanf("%d%d",&a[i],&b[i]);
	}
	
	ans[0][1]=1;
	h=1;
	for (int i=1; i<=n; i++) {
		for (int j=1,mj=h+a[i]-b[i]; j<=mj; j++) {
			LL tmp=0;
			for (int k=1; k<=h; k++) {
				tmp+=ans[i-1][k]*C(a[i]+b[i],j-k+b[i])%Ha;	//核心
				tmp%=Ha;
			}
			ans[i][j]+=tmp;
			ans[i][j]%=Ha;
		}
		h+=a[i]-b[i];
	}
	
	LL Ans=0;
	for (int i=1; i<=h; i++) {
		Ans+=ans[n][i];
		Ans%=Ha;
	}
	
	printf("%lld\n",Ans);
	
	return 0;
}

/*
2
4 2
2 3

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值