题目
二元生成函数与拉格朗日反演:
比较好的形式:对于二元 G F GF GF,第二个元一般来说都是来辅助的,因为一个元用来拉格朗日反演了,另一个元就可以让我们来做多项式操作。
比如二元 G F : GF: GF: H ( u , z ) = 1 1 − u F ( z ) H(u,z) = \frac {1}{1 - uF(z)} H(u,z)=1−uF(z)1, F ( z ) F(z) F(z)有复合逆 G ( z ) G(z) G(z)。
则由于拓展拉格朗日反演: [ z n ] 1 1 − u F ( z ) = 1 n [ z n − 1 ] d 1 1 − u z d z ( z G ( z ) ) n [z^n]\frac 1{1-uF(z)} = \frac 1n[z^{n-1}]\frac {d\frac 1{1-uz}}{dz}(\frac {z}{G(z)})^n [zn]1−uF(z)1=n1[zn−1]dzd1−uz1(G(z)z)n
因为 d 1 1 − u z d z = ( ∑ i = 0 u i z i ) ′ = ∑ i = 0 u i + 1 ( i + 1 ) z i \frac {d\frac 1{1-uz}}{dz} = (\sum_{i=0} u^iz^i) ' = \sum_{i=0} u^{i+1}(i+1)z^i dzd1−uz1=(∑i=0uizi)′=∑i=0ui+1(i+1)zi
所以我们可以在得到 ( z G ( z ) ) n (\frac {z}{G(z)})^n (G(z)z)n后 O ( n ) O(n) O(n)求出 H ( u , z ) [ z n ] = ∑ i = 0 n [ z n ] F ( z ) i u i H(u,z)[z^n] = \sum_{i=0}^n [z^n]F(z)^iu^i H(u,z)[zn]=∑i=0n[zn]F(z)iui。
也就是说我们可以得到 ∀ i ∈ [ 0 , n ] [ z n ] F ( z ) i \forall i\in[0,n] [z^n]F(z)^i ∀i∈[0,n][zn]F(z)i
对于 i > n i \gt n i>n的情况因为 [ z 0 ] F ( z ) = 0 [z^0]F(z) = 0 [z0]F(z)=0,所以 [ z n ] F ( z ) i = 0 [z^n]F(z)^i = 0 [zn]F(z)i=0。
对于没有复合逆的情况也就是:
一: [ z 0 ] F ( z ) = c [z^0]F(z) = c [z0]F(z)=c,则求出 P ( z ) = F ( z ) − c P(z) = F(z) - c P(z)=F(z)−c的之后再二项式展开求出 F ( z ) F(z) F(z)即可。
二: F ( z ) F(z) F(z)的最低次为 k ≥ 2 k \geq 2 k≥2,则将 F ( z ) F(z) F(z)开 k k k次根得到 P ( z ) P(z) P(z)后,求出 [ z n ] P ( z ) i [z^n]P(z)^i [zn]P(z)i,然后再代换回去即可。
原题可以转化为求长度为 n n n的序列中求选出 x = 0 , 1 , 2... n x=0,1,2...n x=0,1,2...n个位置,不存在连续的 k k k个位置被选的方案数。
可以转化为从左到右每次选择 0.... k − 1 0....k-1 0....k−1个位置被选,然后下一个位置强制不选(于是乎我们需要在最后在加一个位置。)
那么这个生成函数就是 F ( z ) = ∑ i = 0 k − 1 z i + 1 = z 1 − z k 1 − z F(z) =\sum_{i=0}^{k-1} z^{i+1} = z\frac {1-z^k}{1-z} F(z)=∑i=0k−1zi+1=z1−z1−zk
选出了 n + 1 − x n+1-x n+1−x个位置,也就是进行了上述的 x x x次操作,所以我们相当于要求
[ z n + 1 ] F ( z ) i , ∀ i ∈ [ 0 , n + 1 ] [z^{n+1}]F(z)^i , \forall i \in[0,n+1] [zn+1]F(z)i,∀i∈[0,n+1]。、
如果有 F ( z ) F(z) F(z)的复合逆我们就可以套用上述做法。
考虑 F ( z ) F(z) F(z)的复合逆 G ( z ) G(z) G(z)满足
G ( z ) k + 1 − G ( z ) = z ( G ( z ) − 1 ) G(z)^{k+1}-G(z) = z(G(z)-1) G(z)k+1−G(z)=z(G(z)−1)
也就是 G ( z ) k + 1 − ( z + 1 ) G ( z ) + z = 0 G(z)^{k+1}-(z+1)G(z)+z = 0 G(z)k+1−(z+1)G(z)+z=0
牛顿迭代:
H ( x ) = x k + 1 − ( z + 1 ) x + z = 0 H(x) = x^{k+1} - (z+1)x+z = 0 H(x)=xk+1−(z+1)x+z=0
x ≡ x 0 − x 0 k + 1 − ( z + 1 ) x 0 + z ( k + 1 ) x 0 k − z − 1 x \equiv x_0 - \frac {x_0^{k+1}-(z+1)x_0+z}{(k+1)x_0^k-z-1} x≡x0−(k+1)x0k−z−1x0k+1−(z+1)x0+z(感谢友善的出题人让分母可以直接求逆。)
所以这个题整个就是个多项式快速幂。
A C C o d e \mathcal AC \ Code AC Code
#include
#define maxn 600005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define mod 998244353
#define LL long long
using namespace std;
int Wl=1,Wl2,w[maxn],lg[maxn],inv[maxn],fac[maxn],invf[maxn];
int Pow(int b,int k){ int r=1;for(;k;k>>=1,b=1ll*b*b%mod) if(k&1) r=1ll*r*b%mod; return r; }
void init(int n){
for(;n>=Wl<<1;Wl<<=1);
int pw = Pow(3,(mod-1)/(Wl2=Wl<<1));
w[Wl] = inv[0] = inv[1] = fac[0] = fac[1] = invf[0] = invf[1] = 1;
rep(i,Wl+1,Wl2) w[i] = 1ll * w[i-1] * pw % mod;
per(i,Wl-1,1) w[i] = w[i<<1];
rep(i,2,Wl2) inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod , fac[i] = 1ll * fac[i-1] * i % mod,
invf[i] = 1ll * invf[i-1] * inv[i] % mod , lg[i] = lg[i>>1] + 1;
}
int upd(int x){ return x += x >> 31 & mod; }
void NTT(int *A,int n,int tp){
static int r[maxn]={};
static unsigned long long ar[maxn];
if(tp ^ 1) reverse(A+1,A+n);
rep(i,0,n-1) r[i] = r[i>>1]>>1|(i&1)<<lg[n]-1,ar[i] = upd(A[r[i]]);
for(int L=1;L<n;L<<=1) for(int s=0,L2=L<<1;s<n;s+=L2) for(int k=s,x=L,t;k<s+L;k++,x++)
t = ar[k+L] * w[x] % mod , ar[k+L] = ar[k] - t + mod , ar[k] += t;
rep(i,0,n-1) A[i] = ar[i] % mod;
if(tp ^ 1) rep(i,0,n-1) A[i] = 1ll * A[i] * inv[n] % mod;
}
void Inv(int *A,int *B,int n){
static int t[maxn];
B[B[1] = 0] = Pow(A[0] , mod-2);
for(int k=2,L=4;k<(n<<1);k<<=1,L<<=1){
rep(i,0,L-1) t[i] = i < k ? A[i] : B[i] = 0;
NTT(t,L,1) , NTT(B,L,1);
rep(i,0,L-1) B[i] = B[i] * (2 - 1ll * t[i] * B[i] % mod) % mod;
NTT(B,L,-1);
rep(i,min(k,n),L-1) B[i] = 0;
}
}
void Ln(int *A,int *B,int n){
static int t[maxn];
int L = 1 << lg[2 * n - 2] + 1;
Inv(A,B,n);
rep(i,0,L-1) t[i] = i < n - 1 ? 1ll * (i + 1) * A[i+1] % mod : B[i] = 0;
NTT(t,L,1) , NTT(B,L,1);
rep(i,0,L-1) t[i] = 1ll * t[i] * B[i] % mod;
NTT(t,L,-1);
B[0] = 0;
rep(i,1,L-1) B[i] = i < n ? 1ll * t[i-1] * inv[i] % mod : 0;
}
void Exp(int *A,int *B,int n){
static int t[maxn];
B[B[1] = 0] = 1;
for(int k=2,L=4;k<(n<<1);k<<=1,L<<=1){
Ln(B,t,k);
rep(i,0,L-1) t[i] = i < k ? ((i == 0) - t[i] + A[i]) % mod : B[i] = 0;
NTT(t,L,1) , NTT(B,L,1);
rep(i,0,L-1) B[i] = 1ll * B[i] * t[i] % mod;
NTT(B,L,-1);
rep(i,min(k,n),L-1) B[i] = 0;
}
}
void Pow(int *A,int *B,int n,int K){
LL x = 0;
for(;x < n && A[x] == 0;x ++);
static int t[maxn];
int L = 1 << lg[n-1] + 1;
rep(i,0,L-1) t[i] = i+x < n ? A[i+x] : 0;
x *= K;
if(x >= n){
rep(i,0,L-1) B[i] = 0;
return;
}
Ln(t,B,L);
rep(i,0,L-1) B[i] = 1ll * K * B[i] % mod;
Exp(B,t,L);
rep(i,0,L-1) B[i] = (i < n && i-x >= 0) ? t[i-x] : 0;
}
void Mul(int *A,int *B,int *C,int n,int m,int cut = 0x3f3f3f3f){
static int st[2][maxn];
int L = 1 << lg[n+m-2] + 1;
rep(i,0,L-1) st[0][i] = i < n ? A[i] : 0 , st[1][i] =i < m ? B[i] : 0;
NTT(st[0],L,1) , NTT(st[1],L,1);
rep(i,0,L-1) C[i] = 1ll * st[0][i] * st[1][i] % mod;
NTT(C,L,-1);
rep(i,cut,L-1) C[i] = 0;
}
void Solve(int *B,int n,int K){
static int t[3][maxn];
B[0] = B[1] = 0;
for(int k=2,L=4;k<(n<<1);k<<=1,L<<=1){
Pow(B,t[1],k,K);
Mul(t[1],B,t[0],k,k,k);
rep(i,0,k-1) t[0][i] = (1ll * t[0][i] - B[i] - (i?B[i-1]:0)) % mod;
t[0][1]++;
rep(i,0,k-1) t[1][i] = (K+1ll) * t[1][i] % mod;
t[1][0] -- , t[1][1] --;
Inv(t[1],t[2],k);
Mul(t[0],t[2],t[1],k,k,k);
rep(i,0,k-1) B[i] = (B[i] - t[1][i]) % mod;
rep(i,min(k,n),L-1) B[i] = 0;
}
}
#define poly vector
#define Ct const
poly Mul(Ct poly &A,Ct poly &B){
static int st[2][maxn];
int L = 1 << lg[A.size() + B.size() - 2] + 1;
rep(i,0,L-1) st[0][i] = i < A.size() ? A[i] : 0 , st[1][i] =i < B.size() ? B[i] : 0;
NTT(st[0],L,1) , NTT(st[1],L,1);
rep(i,0,L-1) st[0][i] = 1ll * st[0][i] * st[1][i] % mod;
NTT(st[0],L,-1);
poly C;C.resize(A.size() + B.size() - 1);
rep(i,0,A.size()+B.size()-2) C[i] = st[0][i];
return C;
}
int m,K,n;
int G[maxn] , cnt[maxn] , vis[maxn] , A[maxn] , B[maxn];
struct cmp{
bool operator ()(const poly &u,const poly &v){
return u.size() > v.size();
}
};
priority_queue<poly,vector<poly>,cmp>q;
int main(){
scanf("%d%d",&m,&K);
rep(i,1,m){
int x;
scanf("%d",&x);
vis[x] = 1;
n = max(n , x);
}
init(2 * (m + 2));
Solve(G,m+2,K);
for(int i=1,j;i<=n;i=j){
for(j = i;j <=n && vis[i] == vis[j];j++);
if(vis[i]) cnt[j - i] ++;
}
rep(i,0,m) G[i] = G[i+1];
rep(i,1,m) if(cnt[i]){
Pow(G,A,i+1,mod-i-1);
poly C(i+1,0);
rep(j,0,i) C[i-j] = (j+1ll) * A[i-j] % mod * inv[i+1] % mod;
rep(j,1,cnt[i]) q.push(C);
}
for(;q.size() >= 2;){
poly u = q.top() ; q.pop();
poly v = q.top() ; q.pop();
q.push(Mul(v,u));
}
poly ans = q.top();
int ret = 0;
rep(i,0,min((int)ans.size()-1,m))
ret = (ret + 1ll * m * inv[m - i] % mod * invf[m] % mod * fac[i] % mod * fac[m - i] % mod * ans[i]) % mod;
printf("%d\n",(ret+mod)%mod);
}
P S : PS: PS:这个做法被直接分治 F F T FFT FFT时间效率上吊打。
我今天又模拟赛又做到一个类似这个做法的题,标算是拉格朗日反演但是被分治FFT吊打,估计得等超快exp普及了之后才能反超。