[数学记录]Loj#6703. 小 Q 的序列
command_block · · 个人记录
戴项式入门题。做本题之前,请先跟我大喊
还有
题意 : 定义一个序列的权值为
给出一个长度为
或者,对于长度为
答案对
参考文献 : https://codeforces.com/blog/entry/76447
题意非常自然,但是第一眼看去并没有什么直接的思路。
直接考虑一个naive的DP,设
有转移 :
仍然不会,考虑套路地写出OGF,令
我们发现
我们考虑变换一下第二维,令
有转移 :
设
然后我们就有多项式形式的转移 :
-
希望分别求和
三个项并不是很好,考虑把两个带
x 的项合并。求导又要和不求导的合并,还带个
- 号,使用坚挺的e^{-x} 吧!F_i(x)e^{-x}=xF_{i-1}(x)e^{-x}+b_iF_{i-1}(x)e^{-x}-xF'_{i-1}(x)e^{-x} =x(F_{i-1}(x)e^{-x}-F'_{i-1}(x)e^{-x})+b_iF_{i-1}(x)e^{-x} 回忆 :
\big(F(x)*G(x)\big)'=F'(x)*G(x)+F(x)*G'(x) 那么就有
(F(x)e^{-x})'=F'(x)e^{-x}-F(x)e^{-x} 前面一切精妙的构造就是为了凑出这个玩意。
\text{原式}=x(F_{i-1}(x)e^{-x})'+b_iF_{i-1}(x)e^{-x} 为了避免啰嗦,定义
H_i(x)=F_i(x)e^{-x} 。则有更为简洁的转移:
H_i(x)=xH'_{i-1}(x)+b_iH_{i-1} 现在可以考虑系数,得到 :
H[i][j]=j*H[i-1][j]+b_i*H[i-1][j]=(j+b_i)H[i-1][j] 出人意料地简洁! 由于次数是对齐的,现在我们能观察到 :
H[n][j]=H[0][j]*\prod_{i=1}^n(j+b_i) 定义
P(x)=\prod_{i=1}^n(x+b_i) ,然后跑个多点求值就好。复杂度O(n\log^2n) 。这样可以做到对长度为
1...n 的子序列分别求和。 -
只希望整体求和
如果我们只希望整体求和,可以直接大力搞那个三项式。
仍然考虑计算
让我们考虑
设
这和第二类斯特林数的递推式十分相似,考虑第二类斯特林数递推的组合意义。
“
意思是,新加入一个球可以自成一盒,也可以投入前面的任何一个盒子里。
现在就相当于,每往盒子里投多一个数,就要把贡献取反。
这就能得到
考虑计算
我们就是要求这个多项式,设为
直接算并不容易,考虑前置 EGF :
- 回忆 :
\frac{1}{k!}(e^x-1)^k=\sum\limits_{k=0}\begin{Bmatrix}k\\i\end{Bmatrix}\dfrac{x^k}{k!}
原式
然后枚举
于是乎跑个
最后的答案就是
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<ctime>
#define ll long long
#define ull unsigned long long
#define clr(f,n) memset(f,0,sizeof(int)*(n))
#define cpy(f,g,n) memcpy(f,g,sizeof(int)*(n))
using namespace std;
const int _G=3,mod=998244353,Maxn=135000;
inline int read(){
int X=0;char ch=0;
while(ch<48||ch>57)ch=getchar();
while(ch>=48&&ch<=57)X=X*10+(ch^48),ch=getchar();
return X;
}
ll powM(ll a,int t=mod-2){
ll ans=1;
while(t){
if(t&1)ans=ans*a%mod;
a=a*a%mod;t>>=1;
}return ans;
}
const int invG=powM(_G);
int tr[Maxn],tf;
void tpre(int n){
if (tf==n)return ;tf=n;
for(int i=0;i<n;i++)
tr[i]=(tr[i>>1]>>1)|((i&1)?n>>1:0);
}
void NTT(int *g,bool op,int n)
{
tpre(n);
static ull f[Maxn],w[Maxn>>1]={1};
for (int i=0;i<n;i++)f[i]=(((ll)mod<<4)+g[tr[i]])%mod;
for(int l=1;l<n;l<<=1){
ull tG=powM(op?_G:invG,(mod-1)/(l+l));
for (int i=1;i<l;i++)w[i]=w[i-1]*tG%mod;
for(int k=0;k<n;k+=l+l)
for(int p=0;p<l;p++){
int tt=w[p]*f[k|l|p]%mod;
f[k|l|p]=f[k|p]+mod-tt;
f[k|p]+=tt;
}
if (l==(1<<10))
for (int i=0;i<n;i++)f[i]%=mod;
}if (!op){
ull invn=powM(n);
for(int i=0;i<n;++i)
g[i]=f[i]%mod*invn%mod;
}else for(int i=0;i<n;++i)g[i]=f[i]%mod;
}
void px(int *f,int *g,int n)
{for(int i=0;i<n;++i)f[i]=1ll*f[i]*g[i]%mod;}
int G[Maxn],s1[Maxn],s2[Maxn<<1];
void cdq(int l,int r)
{
if (l+1==r){
if (l>0)G[l]=powM(l)*G[l]%mod;
return ;
}if (l+1==r)return ;
int mid=(l+r)>>1,n=r-l;
cdq(l,mid);
cpy(s1,G+l,n/2);
if (n>=64){
clr(s1+(n/2),n/2);NTT(s1,1,n);
px(s1,s2+n,n);NTT(s1,0,n);
for (int i=n/2;i<n;i++)
G[l+i]=(G[l+i]+s1[i])%mod;
}else {
for (int i=n/2;i<n;i++)
for (int j=0;j<(n>>1);j++)
G[l+i]=(G[l+i]+1ll*s1[j]*s2[i-j])%mod;
}cdq(mid,r);
}
void exp(int *F,int m)
{
G[0]=1;
for (int i=0;i<m;i++)F[i]=1ll*i*F[i]%mod;
int n=1;
for (;(n>>1)<m;n<<=1){
cpy(s1,F,n);NTT(s1,1,n);
cpy(s2+n,s1,n);
}for (int i=0;i<64;i++)s2[i]=F[i];
cdq(0,n>>=1);cpy(F,G,m);
}
int fac[Maxn],ifac[Maxn];
void Init(int lim)
{
fac[0]=1;
for (int i=1;i<=lim;i++)
fac[i]=1ll*fac[i-1]*i%mod;
ifac[lim]=powM(fac[lim]);
for (int i=lim;i;i--)
ifac[i-1]=1ll*ifac[i]*i%mod;
}
int _s[Maxn<<1],*tp=_s;
struct Data{int *f,c;}t[Maxn];
int n,F[Maxn];
int main()
{
Init(n=read());
for (int i=1,x;i<=n;i++){
x=read();
t[i].f=tp;tp+=(t[i].c=2);
t[i].f[0]=x+i;t[i].f[1]=1;
}
int tot=n;
while(tot>1){
for (int i=2+(tot&1);i<=tot;i+=2){
int c1=t[i-1].c,c2=t[i].c;
cpy(s1,t[i-1].f,c1);cpy(s2,t[i].f,c2);
if (1ll*c1*c2<=26ll*max(c1,c2)){
clr(F,c1+c2-1);
for (int i=0;i<c1;i++)
for (int j=0;j<c2;j++)
F[i+j]=(F[i+j]+1ll*s1[i]*s2[j])%mod;
cpy(t[i-1].f,F,c1+c2-1);
}else {
int n=1;for (;n<c1+c2;n<<=1);
clr(s1+c1,n-c1);clr(s2+c2,n-c2);
NTT(s1,1,n);NTT(s2,1,n);
px(s1,s2,n);NTT(s1,0,n);
cpy(t[i-1].f,s1,c1+c2-1);
}t[i-1].c=c1+c2-1;
t[(i+1)/2]=t[i-1];
}tot=(tot+1)/2;
}
for (int i=0;i<=n;i++)
F[i]=(i&1) ? ifac[i] : mod-ifac[i];
F[0]++;exp(F,n+1);
int ans=0;
for (int i=0;i<=n;i++)
ans=(ans+1ll*F[i]*fac[i]%mod*_s[i])%mod;
printf("%d",(ans-1+mod)%mod);
return 0;
}
要注意的是,分治FFT和半在线卷积时,小规模NTT的常数是在复杂度瓶颈上的。规模小时暴力卷积可以有效地优化常数。
搞一搞就最优解了 : 评测记录