分拆数
将n拆分成任意个无标号正整数(也可认为是排好序)的方案数记为
暴力DP——
两种考虑方式,第一维都是总和,对于第二维,一种是考虑前j种数,另一种是分成j个数。即第一种是完全背包,第二种是考虑最小的数是否为1转移。
数据分治优化——
注意到两种DP的效率分别是,考虑经典的数据分治优化:
对于前者只考虑在值域内的数;
那么剩下的大于的数,就不会超过,就可以用第二个DP了!
至此,已经得到了比较实用的做法,即码量较小的。
(664B)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,M=505,P=998244353;
void A(int& x,int y){
x+=y;
if(x>=P) x-=P;
}
int n,m,f[N][2],g[N][2],s[N];
int main()
{
cin>>n;
m=(int)(sqrt(n)+0.5);
f[0][0]=1;
for(int j=1;j<=m;j++){
int p=(j&1);
for(int i=0;i<=n;i++){
f[i][p]=f[i][!p];
if(j<=i) A(f[i][p],f[i-j][p]);
if(j==m) g[i][0]=f[i][p],s[i]=f[i][p];
}
}
for(int j=1;j<=n/m;j++){
int p=(j&1);
for(int i=0;i<=n;i++){
if(i>=m+1) g[i][p]=g[i-1-m][!p];
else g[i][p]=0;
if(j<=i) A(g[i][p],g[i-j][p]);
A(s[i],g[i][p]);
}
}
for(int i=1;i<=n;i++) printf("%d\n",s[i]);
return 0;
}
为了追求极致(防止丧病出题人),还是要研究一下最优的log做法。
多项式做法(五边形数定理)——
考虑生成函数,容易发现拆分数对应的多项式是的逆,而这个式子有个神奇的结论:
由于最后的指数是五边形数,故称为五边形数定理。
发现指数是i的平方级别,那么枚举绝对值在内的i(具体的,稍作放缩可得),然后求逆即可。
好像套个板子就没了,比根号的还好写。
(2249B)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=(1<<20)+5,P=998244353,G[2]={3,(P+1)/3};
inline int fpw(int a,int x){
int s=1;
for(;x;x>>=1,a=1ll*a*a%P) if(x&1) s=1ll*s*a%P;
return s;
}
int rv[N],gp[2][N],iv[N],fc[N],fv[N];
void init(int n){
fc[0]=1;
for(int i=1;i<n;i++) iv[i]=fpw(i,P-2),fc[i]=1ll*fc[i-1]*i%P;
fv[n-1]=fpw(fc[n-1],P-2);
for(int i=n-2;~i;i--) fv[i]=1ll*fv[i+1]*(i+1)%P;
for(int p=0;p<2;p++){
for(int i=1;i<n;i<<=1){
gp[p][i]=1;
int t=fpw(G[p],(P-1)/(i<<1));
for(int j=i+1;j<(i<<1);j++) gp[p][j]=1ll*gp[p][j-1]*t%P;
}
}
}
void print(char name[],int n,int* a){
printf("%s n=%d\n",name,n);
for(int i=0;i<n;i++) cout<<a[i]<<" "; puts("");
}
inline void dft(int* a,int n,int p){
for(int i=0;i<n;i++) if(i<rv[i]) swap(a[i],a[rv[i]]);
for(int i=1;i<n;i<<=1){
for(int j=0;j<n;j+=(i<<1)){
for(int k=0;k<i;k++){
int &A=a[i+j+k],&B=a[j+k],t=1ll*gp[p][i+k]*A%P;
A=B-t; if(A<0) A+=P;
B=B+t; if(B>=P) B-=P;
}
}
}
if(p) for(int i=0;i<n;i++) a[i]=1ll*a[i]*iv[n]%P;
}
inline int Rev(int m){
int p=0,n=1;
while(n<m) n<<=1,p++;
for(int i=0;i<n;i++) rv[i]=(rv[i>>1]>>1)|((i&1)<<(p-1));
return n;
}
inline void poly_mul(int m,int* A,int* B,int* C){
int n=Rev(m);
dft(A,n,0); dft(B,n,0);
for(int i=0;i<n;i++) C[i]=1ll*A[i]*B[i]%P;
dft(C,n,1);
fill(C+m,C+n,0);
}
int C[N],D[N];
inline void poly_inv(int m,int *a, int *b) {
if(m==1){
b[0]=fpw(a[0],P-2);
return;
}
poly_inv((m + 1)>>1,a,b);
int n=Rev(m<<1);
copy(a,a+m,C);
fill(C+m,C+n,0);
dft(b,n,0); dft(C,n,0);
for(int i=0;i<n;i++) b[i]=1ll*b[i]*(P+2-1ll*b[i]*C[i]%P)%P;
dft(b,n,1);
fill(b+m,b+n,0);
}
void A(int& x,int y){
x+=y;
if(x>=P) x-=P;
}
int n,m,a[N],b[N];
int main(){
init(1<<19);
cin>>n;
m=(int)(sqrt(n*2/3+1)+0.5);
for(int i=-m;i<=m;i++){
int t=i*(3*i-1)/2;
if(t>n) continue;
if(abs(i)&1) A(a[t],P-1);
else A(a[t],1);
}
poly_inv(n+1,a,b);
for(int i=1;i<=n;i++) printf("%d\n",b[i]);
return 0;
}
分类:
XCPC——计数(组合,多项式)
, XCPC——动态规划
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】