CF896D Nephren Runs a Cinema
CF896D Nephren Runs a Cinema
题意
售票员最开始没有纸币,每次来一个顾客可以给她一张、拿走她一张或不操作。求出不出现中途没钱给的情况 \(n\) 名顾客后剩余钱数在 \(l\sim r\) 的方案数。
思路
这是我们一道模拟赛题。
解法 1:套路组合计数。先不考虑不操作的顾客,那么就相当于是求二维平面不过一条直线到达一点的方案数。直接枚举操作顾客数,用组合数减去另外一个组合数然后乘上所有顾客中选这些顾客的方案数即可。
解法 2:暴力动态规划。每次从至多三个方向转移。
赛时我先想到第一个解法但是没想到枚举操作人数,以为不可做,于是想到了优化暴力的解法。
解法 3:解法 2 的优化。求出 \(F=(x^{-1}+1+x)^n\) ,然后 \(f(x)-f(x+2)\) 的第 \(l \sim r\) 项的和即为答案。
\((x^{-1}+1+x)\) 表示三种转移方式,但是求出来的 \(F\) 显然不是正确答案,因为会有从负数幂的贡献是应该减掉的。
那么我们就考虑减掉它,实际上和解法一相同,\(f(x)-f(x+2)\) 相当于减去了错解,那么减完之后就是答案了。
因为每次要倍长多项式,使用快速幂一样的方式卷就可以了。根据主定理,时间复杂度 \(O(n\log n)\)。
考场上有部分分,模数为 \(998244353\),那么正解只需要改成任意模数多项式乘法就行了。
代码
注意,此代码不可 AC 此题,仅在模数为 \(998244353\) 时正确。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<cstdlib>
using namespace std;
inline int read(){
int x=0,w=0;char c=getchar();
while(!isdigit(c)) w|=c=='-',c=getchar();
while(isdigit(c)) x=x*10+(c^48),c=getchar();
return w?-x:x;
}
namespace star
{
const int maxn=2e6+10,mod=998244353,ge=3,gi=998244354/3;
inline int fpow(int a,int b){int ans=1;for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)ans=1ll*ans*a%mod;return ans;}
struct NTT{
int r[maxn],lim;
inline void getr(int li){lim=li;for(int i=0;i<lim;i++) r[i]=(r[i>>1]>>1)|((i&1)*(lim>>1));}
inline void operator () (int *a,int type) const {
for(int i=0;i<lim;i++) if(i<r[i]) swap(a[i],a[r[i]]);
for(int mid=1;mid<lim;mid<<=1){
int rt=fpow(type==1?ge:gi,(mod-1)/(mid<<1));
for(int r=mid<<1,j=0;j<lim;j+=r){
int p=1;
for(int k=0;k<mid;k++,p=1ll*p*rt%mod){
int x=a[j+k],y=1ll*p*a[j+k+mid]%mod;
a[j+k]=(x+y)%mod,a[j+k+mid]=(x-y+mod)%mod;
}
}
}
if(type==-1) for(int p=fpow(lim,mod-2),i=0;i<lim;i++) a[i]=1ll*a[i]*p%mod;
}
}ntt;
int n;
int a[maxn],ans[maxn];
inline void work(){
n=read();read();
int Lim=4,lim=2;
a[0]=a[1]=a[2]=ans[0]=1;
int x=n,Len=3,len=1;
while(x){
static int res[maxn],tmp[maxn];
if(x&1){
for(int i=0;i<Lim;i++) res[i]=a[i];
for(int i=Lim;i<Lim+lim;i++) res[i]=0;
for(int i=0;i<lim;i++) tmp[i]=ans[i];
for(int i=lim;i<Lim+lim;i++) tmp[i]=0;
len+=Len;lim=Lim;
while(lim<len) lim<<=1;
ntt.getr(lim<<1);
ntt(res,1),ntt(tmp,1);
for(int i=0;i<lim<<1;i++) tmp[i]=1ll*res[i]*tmp[i]%mod;
ntt(tmp,-1);
for(int i=0;i<lim;i++) ans[i]=tmp[i];
}
for(int i=0;i<Lim;i++) res[i]=a[i];
for(int i=Lim;i<Lim<<1;i++) res[i]=0;
Lim<<=1;Len<<=1;
for(int i=Lim;i<Lim<<1;i++) res[i]=0;
ntt.getr(Lim<<1);
ntt(res,1);
for(int i=0;i<Lim<<1;i++) res[i]=1ll*res[i]*res[i]%mod;
ntt(res,-1);
for(int i=0;i<Lim;i++) a[i]=res[i];
x>>=1;
}
int l=read(),r=read();
for(int i=n;i<n<<1;i++) ans[i]=(ans[i]-ans[i+2]+mod)%mod;
int Ans=0;
for(int i=n+l;i<=n+r;i++) Ans=(Ans+ans[i])%mod;
printf("%d\n",Ans);
}
}
signed main(){
star::work();
return 0;
}