【JZOJ6151】星际穿越

40 篇文章 0 订阅

Description

在这里插入图片描述

Solution

f i f_i fi表示前 i K iK iK列的答案,考虑容斥,枚举有多少段连续上升的段,则有:
f i = ∑ j = 0 i − 1 ( i K j K ) r ( − 1 ) i − j − 1 f j f_i=\sum_{j=0}^{i-1}\binom{iK}{jK}^r(-1)^{i-j-1}f_j fi=j=0i1(jKiK)r(1)ij1fj
拆开组合数,移项得:
f i ( ( i K ) ! ) r = ∑ j = 0 i − 1 f j ( ( j K ) ! ) r ⋅ ( − 1 ) i − j − 1 ( ( ( i − j ) k ) ! ) r \dfrac{f_i}{((iK)!)^r}=\sum_{j=0}^{i-1}\dfrac{f_j}{((jK)!)^r}\cdot\dfrac{(-1)^{i-j-1}}{(((i-j)k)!)^r} ((iK)!)rfi=j=0i1((jK)!)rfj(((ij)k)!)r(1)ij1
F ( x ) = ∑ i = 0 ∞ f i ( ( i K ) ! ) r , G ( x ) = ∑ i = 1 ∞ ( − 1 ) i − 1 ( ( i K ) ! ) r F(x)=\sum_{i=0}^{\infin}\dfrac{f_i}{((iK)!)^r},G(x)=\sum_{i=1}^{\infin}\dfrac{(-1)^{i-1}}{((iK)!)^r} F(x)=i=0((iK)!)rfiG(x)=i=1((iK)!)r(1)i1
F ( x ) = F ( x ) G ( x ) + 1 F(x)=F(x)G(x)+1 F(x)=F(x)G(x)+1,即 F ( x ) = 1 1 − G ( x ) F(x)=\dfrac{1}{1-G(x)} F(x)=1G(x)1
多项式求逆。至于最后不足K的一段暴力卷积即可。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
using namespace std;
typedef long long ll;
const int N=1e6+10,mo=998244353;
int qpow(int x,int y){
	int s=1;
	for(;y;y>>=1,x=(ll)x*x%mo) if(y&1) s=(ll)s*x%mo;
	return s;
}
int pl(int x,int y){
	return x+y>=mo?x+y-mo:x+y;
}
void inc(int &x,int y){
	x=x+y>=mo?x+y-mo:x+y;
}
int rev[N*4],fn;
void NTT(int *a,int sig){
	fo(i,1,fn-1) if(i<rev[i]) swap(a[i],a[rev[i]]);
	for(int m=2;m<=fn;m<<=1){
		int hf=m>>1,w0=qpow(3,(mo-1)/m);
		if(sig<0) w0=qpow(w0,mo-2);
		for(int i=0;i<fn;i+=m)
		for(int j=i,w=1;j<i+hf;++j,w=(ll)w*w0%mo){
			int u=a[j],v=(ll)a[j+hf]*w%mo;
			a[j]=pl(u,v),a[j+hf]=pl(u,mo-v);
		}
	}
	if(sig<0){
		int nn=qpow(fn,mo-2);
		fo(i,0,fn-1) a[i]=(ll)a[i]*nn%mo;
	}
}
int c[N*4];
void inv(int ln,int *a,int *b){
	if(ln==1){
		b[0]=qpow(a[0],mo-2);
		return;
	}
	inv((ln+1)>>1,a,b);
	int cnt=0;
	for(fn=1;fn<(ln<<1);fn<<=1) ++cnt;
	fo(i,1,fn-1) rev[i]=rev[i>>1]>>1|(i&1)<<(cnt-1);
	fo(i,0,ln-1) c[i]=a[i];
	fo(i,ln,fn-1) c[i]=b[i]=0;
	NTT(c,1),NTT(b,1);
	fo(i,0,fn-1) b[i]=(2-(ll)c[i]*b[i]%mo+mo)%mo*b[i]%mo;
	NTT(b,-1);
	fo(i,ln,fn-1) b[i]=0;
}
int jc[N],ny[N];
int f[N*4],a[N],g[N];
int main()
{
	freopen("interstellar.in","r",stdin);
	freopen("interstellar.out","w",stdout);
	int n,r,k,t;
	scanf("%d %d %d",&n,&r,&k),t=n/k;
	jc[0]=1;
	fo(i,1,n) jc[i]=(ll)jc[i-1]*i%mo;
	ny[n]=qpow(jc[n],mo-2);
	fd(i,n,1) ny[i-1]=(ll)ny[i]*i%mo;
	a[0]=1;
	fo(i,1,t) a[i]=(mo-qpow(ny[i*k],r)*(i&1?1:-1))%mo,g[i]=mo-a[i];
	inv(t+1,a,f);
	int ans=0;
	if(n%k){
		fo(i,0,t){
			ll w=(qpow(ny[n-i*k],r)*((t-i)&1?-1:1)+mo)%mo;
			inc(ans,(ll)f[i]*w%mo);
		}
	}
	else ans=f[t];
	ans=(ll)ans*qpow(jc[n],r)%mo;
	printf("%d",ans);
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值