题解 P5388 【[Cnoi2019]最终幻想】

cosmicAC

2019-11-01 08:03:46

Solution

这题一看就是要求$F(n,k)=\sum_{i=0}^{n}\binom{k}{i}$。然而怎么只有21分?原来n最大可以到mod-1。 于是考虑经典的分块打表。记录一部分的$F(n,k)$,根据洛谷的代码上限50KB,可以计算得到最多能记录10000个数不到。所以我们可以每隔1e7个数记录一个$F(n,k)$。(**打表程序需要求$1\ldots mod-1$的逆元,并且复杂度是$O(100\times mod)$,所以需要很长的时间与大概4G的空间。**) 然后问题就变成了:给定$F(n,k)$,求$F(n+\Delta n,k+\Delta k)$。这个问题可以拆成两个部分,就是从$F(n,k)$求$F(n,k+1)$和$F(n+1,k)$。 从$F(n,k)$求$F(n+1,k)$很简单,就是加上一个$\binom{k}{n+1}$。然后考虑到$\binom{k+1}{n}=\binom{k}{n}+\binom{k}{n-1}$,有 $$F(n,k+1)=\sum_{i=0}^{n}\binom{k+1}{i}$$ $$=\sum_{i=0}^{n}\binom{k}{i}+\binom{k}{i-1}$$ $$=\sum_{i=0}^{n}\binom{k}{i}+\sum_{i=0}^{n-1}\binom{k}{i}$$ $$=2F(n,k)-\binom{k}{n}$$ 所以问题被转化为了求一行组合数和一列组合数。先再次打表记录$n!$,暴力求出一个组合数。然后考虑从$\binom{n}{k}$变成$\binom{n}{k+1}$和$\binom{n+1}{k}$,都只要乘一个数再除以一个数,就只需要求2e7个数的逆元,直接离线线性求逆即可。 代码如下: ```cpp #include<bits/stdc++.h> using namespace std; using ll=long long; const int siz=10507835; const ll mod=998244353; //省略打表数组 int n,k,inv1[siz+10],inv2[siz+10]; ll po(ll a,ll b){ll r=1;for(;b;b/=2,a=a*a%mod)if(b&1)r=r*a%mod;return r;} ll fac(int x){ ll r=fc[x/siz]; for(int i=x/siz*siz+1;i<=x;i++)(r*=i)%=mod; return r; } ll C(int x,int y){return fac(x)*po(fac(y),mod-2)%mod*po(fac(x-y),mod-2)%mod;} int main(){ scanf("%d%d",&n,&k); if(n>=k)printf("%lld\n",po(2,k)),exit(0); int kk=k/siz*siz,nn=n/siz*siz; inv1[0]=inv2[0]=1; for(int i=1;i<=k-kk;i++)inv1[i]=1ll*inv1[i-1]*(i+kk-nn)%mod; ll now=po(inv1[k-kk],mod-2);if(k>kk)inv1[k-kk]=now*inv1[k-kk-1]%mod; for(int i=k-kk-1;i>0;i--) (now*=i+1+kk-nn)%=mod,inv1[i]=now*inv1[i-1]%mod; for(int i=1;i<=n-nn;i++)inv2[i]=1ll*inv2[i-1]*(i+nn)%mod; now=po(inv2[n-nn],mod-2);if(n>nn)inv2[n-nn]=now*inv2[n-nn-1]%mod; for(int i=n-nn-1;i>0;i--) (now*=i+1+nn)%=mod,inv2[i]=now*inv2[i-1]%mod; ll res=db[k/siz][n/siz];now=C(kk,nn); for(int t=kk;t<k;t++){ res=(2*res-now)%mod; (now*=1ll*(t+1)*inv1[t-kk+1]%mod)%=mod; } for(int t=nn;t<n;t++){ (now*=1ll*inv2[t-nn+1]*(k-t)%mod)%=mod; (res+=now)%=mod; } printf("%lld\n",(res+mod)%mod); return 0; } ```