AtCoder Beginner Contest 240 G.Teleporting Takahashi(组合数学 经典问题)

题目

在三维空间内,你当前处于(0,0,0),需要走到(x,y,z)(-1e7<=x,y,z<=1e7)

每一步,你可以先选择三维中的某一维,然后令这一维+1或-1,其他两维保持不变,

换言之,每一步只能从当前整点移动到三维空间内相邻的整点,

求恰好n(n<=1e7)步走到(x,y,z)的方案数,答案对998244353取模

思路来源

dls的B站讲解

题解

首先,题目n=1e7,刻意卡了NTT,因为998244353=2^23*7*17,

最大支持的NTT长度为2^23,再大下去变换会失真从而无法确定函数,

比如长度为2^24,则可能会出现两个位置分到同一个复根的情况,

当然,折半之后NTT再合并答案,可以强行卡过去

所以,考虑这题O(n)怎么做,

判完不合法之后,很容易想到枚举其中一维x,

从n步里选i步给x这维,求i步里走x的方案数,

剩下n-i步留给(y,z)这两维,求n-i步在一维走y另一维走z的方案数

在二维空间下,这是一个经典问题

考虑对二维空间(y,z)进行(-1,0)(0,-1)(1,0)(0,1)四种操作,

等价于对二维空间(y+z,y-z)进行(-1,-1)(-1,1)(1,1)(1,-1)四种操作,

原来的空间内只能对某一维+1或-1,

而在新的空间内两维的+1和-1互不影响,是独立的,

且两个空间是满射的,即一一映射,

新空间独立找出的方案,可以唯一映射回老空间内对应的方案

根据乘法原理,只需要分别求n-i步走y+z和y-z的方案数,相乘即可

代码

#include<bits/stdc++.h>
using namespace std;
const int mod=998244353; 
const int N=1e7+10;
int Finv[N],fac[N],inv[N];
int n,x,y,z,ans;
int modpow(int x,int n,int mod){
	int res=1;
	for(;n;x=1ll*x*x%mod,n>>=1)
	if(n&1)res=1ll*res*x%mod;
	return res;
}
void init(int n){ //n<N
    inv[1]=1;
    for(int i=2;i<=n;++i)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	fac[0]=Finv[0]=1;
	for(int i=1;i<=n;++i)fac[i]=1ll*fac[i-1]*i%mod,Finv[i]=1ll*Finv[i-1]*inv[i]%mod;
	//Finv[n]=modpow(fac[n],mod-2,mod);
	//for(int i=n-1;i>=1;--i)Finv[i]=1ll*Finv[i+1]*(i+1)%mod;
}
int C(int n,int m){
	if(m<0||m>n)return 0;
	return 1ll*fac[n]*Finv[n-m]%mod*Finv[m]%mod;
}
//x步恰在某一维走y步 
int cal(int x,int y){
	if(x<y || ((x-y)&1))return 0;
	return C(x,(x+y)/2);
}
//x步恰在某一维走y步且另一维走z步 
int cal2(int x,int y,int z){
	if(x<y+z || ((x-y-z)&1))return 0;
	return 1ll*cal(x,y+z)*cal(x,abs(y-z))%mod;
}
int main(){
    init(N-5);
    scanf("%d%d%d%d",&n,&x,&y,&z);
    x=abs(x);y=abs(y);z=abs(z);
    if(x+y+z>n || ((n-x-y-z)&1)){
    	puts("0");
    	return 0;
    }
    //枚举x这维用多少步 剩下的步数交给y和z 
    for(int i=0;i<=n;++i){
    	ans=(ans+1ll*C(n,i)*cal(i,x)%mod*cal2(n-i,y,z)%mod)%mod;
    }
    printf("%d\n",ans);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值