Time Limit 2s
Memory Limit 512M
Description
从1到n中选k个互不相同的,问一起为边长能组成凸k边形的方案数
Input
一行,n,k
Output
一行答案对998244353取模
Sample
Input1
100 3
Output1
79625
Input2
100 4
Output2
3281225
Input3
100 5
Output3
72464552
Data Constraint
n≤109,k≤200n\leq 10^9,k\leq 200n≤109,k≤200
什么凸多边形。。。搞得很难的样子,就是求选取方案种数使得最小的k-1个数的和大于第k个
考虑O(nk)O(nk)O(nk)怎么做
Ans=(nk)−∑i=1n(n−i+1)fi,k−1Ans={n\choose k}-\sum_{i=1}^n (n-i+1)f_{i,k-1}Ans=(kn)−∑i=1n(n−i+1)fi,k−1(最大的数>=除最大数的和=i)
fi,jf_{i,j}fi,j表示选j个拼i的方案数
假设选出来的a1<a2<⋯<aka_1<a_2<\cdots<a_ka1<a2<⋯<ak
其差分d1,d2,⋯ ,dk>0d_1,d_2,\cdots,d_k>0d1,d2,⋯,dk>0
那么(k−1)d1+(k−2)d2+⋯+dk−1=i(k-1)d_1+(k-2)d_2+\cdots+d_{k-1}=i(k−1)d1+(k−2)d2+⋯+dk−1=i
因此这可以背包
考虑生成函数
fi,k−1=[xi]∏i=1k−1xi1−xif_{i,k-1}=[x^i]\prod_{i=1}^{k-1}\frac{x^i}{1-x^i}fi,k−1=[xi]∏i=1k−11−xixi
因为id(i)=i+1id(i)=i+1id(i)=i+1的OGF是1(1−x)2\frac 1{(1-x)^2}(1−x)21
∑i=1n(n−i+1)fi,k−1\sum_{i=1}^n (n-i+1)f_{i,k-1}∑i=1n(n−i+1)fi,k−1是个卷积
(nk)−Ans=[xn][1(1−x)2∏i=1k−1xi1−xi]=[xn−12k(k−1)][1(1−x)2∏i=1k−11−xi]{n\choose k}-Ans=[x^n][\frac1{(1-x)^2}\prod_{i=1}^{k-1}\frac{x^i}{1-x^i}]=[x^{n-\frac12k(k-1)}][\frac1{(1-x)^2\prod_{i=1}^{k-1}1-x^i}](kn)−Ans=[xn][(1−x)21∏i=1k−11−xixi]=[xn−21k(k−1)][(1−x)2∏i=1k−11−xi1]
设m=n−12k(k−1)m=n-\frac12k(k-1)m=n−21k(k−1)
我们发现下面的(1−x)2∏i=1k−11−xi(1-x)^2\prod_{i=1}^{k-1}1-x^i(1−x)2∏i=1k−11−xi可以表示成1−G(x)1-G(x)1−G(x)的形式
这样就可以用线性常系数齐次递推,设(nk)−Ans=F(x)=11−G(x){n\choose k}-Ans=F(x)=\frac 1{1-G(x)}(kn)−Ans=F(x)=1−G(x)1
G(x)G(x)G(x)的最高次项为xtx^txt
那么F(x)G(x)=F(x)−1F(x)G(x)=F(x)-1F(x)G(x)=F(x)−1
又G(x)常数项为0,那么设{fn}\{f_n\}{fn}为F(x)的系数数列,{gn}\{g_n\}{gn}同理,有fn=∑i=1nfn−igif_n=\sum_{i=1}^n f_{n-i}g_ifn=∑i=1nfn−igi,这可以看做一个递推
设关于F(x)的矩阵AAA
A=[f1f2⋮ft]
A=
\begin{bmatrix}
f_1\\
f_2\\
\vdots\\
f_t\\
\end{bmatrix}
A=⎣⎢⎢⎢⎡f1f2⋮ft⎦⎥⎥⎥⎤
设其转移矩阵为TTT
T=[g1g2⋯gt−1gt10⋯0001⋯00⋮⋮⋱⋮⋮00⋯10]
T=
\begin{bmatrix}
g_1 & g_2 & \cdots & g_{t-1} & g_t\\
1 & 0 & \cdots & 0 & 0 \\
0 & 1 & \cdots & 0 & 0\\
\vdots & \vdots & \ddots & \vdots & \vdots\\
0 & 0 & \cdots & 1 & 0\\
\end{bmatrix}
T=⎣⎢⎢⎢⎢⎢⎡g110⋮0g201⋮0⋯⋯⋯⋱⋯gt−100⋮1gt00⋮0⎦⎥⎥⎥⎥⎥⎤
那么要求的fmf_mfm可以通过求ATm−tAT^{m-t}ATm−t得
假设我们要求TmT^mTm
但是t很大,m也很大
有一个叫做凯莱·哈密顿定理的东西
T的特征多项式r(λ)=det(λI−T)r(\lambda)=\det(\lambda I-T)r(λ)=det(λI−T)
则r(T)=0r(T)=0r(T)=0
特殊的,对于上面这类型的矩阵,其特征多项式为r(x)=xt−∑i=1tgixt−iir(x)=x^t-\sum_{i=1}^tg_ix^{t-i}ir(x)=xt−∑i=1tgixt−ii
r(x)r(x)r(x)只有t项!
如果求出xmmod  r(x)x^m\mod r(x)xmmodr(x)再把T带进去就可以把指数大大缩小
这个可以倍增+多项式取模求
假设已经求出了h(x)=xmmod  r(x)h(x)=x^m\mod r(x)h(x)=xmmodr(x)
但是t很大怎么办
考虑回原式子,我们需要求的是[Ah(T)][n][Ah(T)][n][Ah(T)][n]
又有ft+i=(ATi)[n]f_{t+i}=(AT^i)[n]ft+i=(ATi)[n]
这样就只用求∑ft+i[xi]h(x)\sum f_{t+i}[x^i]h(x)∑ft+i[xi]h(x)即可
只用求逆弄出f的前2t位,取模搞出个h(x)h(x)h(x)就可以求答案了
多项式取模(求商)
设∣f(x)∣|f(x)|∣f(x)∣表示f(x)f(x)f(x)的最高次数
假设要求A(x)mod  B(x)A(x)\mod B(x)A(x)modB(x)
A(x)=B(x)C(x)+D(x)A(x)=B(x)C(x)+D(x)A(x)=B(x)C(x)+D(x),且∣D(x)∣<∣B(x)∣|D(x)|<|B(x)|∣D(x)∣<∣B(x)∣
设∣A(x)∣=n,∣B(x)∣=m|A(x)|=n,|B(x)|=m∣A(x)∣=n,∣B(x)∣=m
有A(1x)=B(1x)C(1x)+D(1x)A(\frac1x)=B(\frac1x)C(\frac1x)+D(\frac1x)A(x1)=B(x1)C(x1)+D(x1)
同时乘上xnx^nxn
xnA(x)=xmB(x)×xn−mC(x)+xnD(x)x^nA(x)=x^mB(x)\times x^{n-m}C(x)+x^nD(x)xnA(x)=xmB(x)×xn−mC(x)+xnD(x)
xnA(x),xmB(x),xn−mC(x)x^nA(x),x^mB(x),x^{n-m}C(x)xnA(x),xmB(x),xn−mC(x)都相当于将他们反过来
只有D(x)D(x)D(x)的前n-m项系数都是0,
两边同时mod  xn−m\mod x^{n-m}modxn−m
消去D′(x)D'(x)D′(x)(这里用“′'′”表示将其反过来的多项式)变为A′(x)=B′(x)C′(x)(mod  xn−m)A'(x)=B'(x)C'(x)(\mod x^{n-m})A′(x)=B′(x)C′(x)(modxn−m)
C′(x)=A′(x)B′(x)mod  xn−mC'(x)=\frac{A'(x)}{B'(x)}\mod x^{n-m}C′(x)=B′(x)A′(x)modxn−m用一个多项式求逆
然后再乘回去求出D(x)D(x)D(x)
#include<cstring>
#include<cstdio>
#include<algorithm>
#define mo 998244353
#define K 262144
#define ll long long
#define REP(i,a,b) for(int i=a,___=b;i<___;++i)
#define RED(i,a,b) for(int i=a,___=b;--i>=___;)
using namespace std;
int N,k,G[K],F[K],t,w[K],rev[K],D[K];
ll n,m;
ll qpow(ll a,ll i){
ll r=1;for(;i;i>>=1,a=a*a%mo)if(i&1)r=r*a%mo;return r;
}
void adjust(int &N,int l){
for(;N<l;N<<=1);for(;N>>1>=l;N>>=1);
}
void prep(){
ll W=qpow(3,(mo-1)/K);w[0]=1;REP(i,1,K)w[i]=W*w[i-1]%mo;
}
void ntt(int *a){
REP(i,1,N){
rev[i]=(rev[i>>1]>>1)+(i&1)*(N>>1);
if(rev[i]<i)swap(a[i],a[rev[i]]);
}ll T;
for(int h=1,m=2,g=1;h<N;h=m,m<<=1,g++)REP(i,0,h)for(int j=i,k;j<N;j+=m)
T=1ll*w[(K>>g)*i]*a[k=h+j],a[k]=(a[j]-T)%mo,a[j]=(a[j]+T)%mo;
}
void intt(int *a){
REP(i,1,N>>1)swap(a[i],a[N-i]);ntt(a);
ll n=qpow(N,mo-2);REP(i,0,N)a[i]=n*a[i]%mo;
}
void inv(int *a){
static int b[K],c[K],n;n=N;
REP(i,1,N+N)b[i]=c[i]=0;
b[0]=qpow(a[0],mo-2);
for(N=2;N<=n;){
REP(i,0,N)c[i]=a[i];N<<=1;
ntt(c);ntt(b);
REP(i,0,N)b[i]=(2-1ll*b[i]*c[i])%mo*b[i]%mo;
intt(b);REP(i,N>>1,N)b[i]=0;
}N>>=1;REP(i,0,N)a[i]=b[i];
}
void mod(int *a,int na){
static int c[K],d[K],nc,nb;
nb=t;nc=na-nb+1;
if(na<nb)return;
REP(i,0,nc)c[i]=a[na-1-i],d[i]=D[i];
adjust(N,nc+nc-1);REP(i,nc,N)d[i]=c[i]=0;
ntt(c);ntt(d);REP(i,0,N)c[i]=1ll*c[i]*d[i]%mo;
intt(c);
adjust(N,na);REP(i,nc,N)c[i]=0;
REP(i,0,nb)d[i]=G[i];REP(i,nb,N)d[i]=0;
ntt(c);ntt(d);REP(i,0,N)c[i]=1ll*c[i]*d[i]%mo;
intt(c);
REP(i,0,na)a[i]=(a[i]-c[na-1-i])%mo;
}
//倍增用递归版的会更快
int qmod(ll m,int *a,int *r){
if(m<t){r[m]=1;return m+1;}
int l=qmod(m/2,a,r);adjust(N,l<<=1);
ntt(r);REP(i,0,N)r[i]=1ll*r[i]*r[i]%mo;intt(r);
if(m&1){RED(i,N,0)r[i+1]=r[i];r[0]=0;}else --l;
mod(r,l);return min(l,t-1);
}
int c(int n,int m){
int r=1;REP(i,1,m+1)r=1ll*r*qpow(i,mo-2)%mo*(n+1-i)%mo;return r;
}
int main(){
scanf("%lld %d",&n,&k);
if(n<k){printf("0");return 0;}
int C=c(n,k);
if(n<k*(k-1)/2){printf("%d",C);return 0;}
prep();
G[0]=G[2]=1;G[1]=mo-2;t=3;
REP(i,1,k){
RED(j,t,0)G[i+j]=(G[i+j]-G[j])%mo;t+=i;
}N=1;adjust(N,t+t);
REP(i,0,N)F[i]=G[i],D[i]=G[i];inv(F);
n-=k*(k-1)/2;m=n-t+1;
if(n<t+t){
printf("%d",(1ll*C+mo-F[n])%mo);
return 0;
}
//没有对其操作是因为特征多项式要反过来,再翻一次就恢复原样
inv(D);//先预先反过来求个逆,不然倍增一次次求太慢
static int r[K];
REP(i,0,t+t)r[i]=0;
qmod(m,G,r);
int ans=0;
REP(i,0,t)ans=(ans+1ll*r[i]*F[t-1+i])%mo;
printf("%d",(1ll*C+mo-ans)%mo);
}