BZOJ 1025: [SCOI2009]游戏 [置换群 DP]
题意:求$n$个数组成的排列变为升序有多少种不同的步数
步数就是循环长度的$lcm$.....
那么就是求$n$划分成一些数几种不同的$lcm$咯
然后我太弱了这种$DP$都想不出来....
通过枚举每个质因子的指数来求$lcm$
$d[i][j]$表示前$i$个质因子当前和为$j$的方案数
转移枚举质因子的指数
但这样我们忽略了可以划分出$1$,所以统计答案时枚举$j$
或者我们直接初始化$d[0][i]=1$
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; const int N=1005; typedef long long ll; inline int read(){ char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();} return x*f; } int n; int p[N]; bool notp[N]; void sieve(int n){ for(int i=2;i<=n;i++){ if(!notp[i]) p[++p[0]]=i; for(int j=1;j<=p[0]&&i*p[j]<=n;j++){ notp[i*p[j]]=1; if(i%p[j]==0) break; } } } ll f[N][N]; void dp(){ f[0][0]=1; for(int i=1;i<=p[0];i++) for(int j=0;j<=n;j++){ f[i][j]=f[i-1][j]; for(int k=p[i];k<=j;k*=p[i]) f[i][j]+=f[i-1][j-k]; } ll ans=0; for(int i=0;i<=n;i++) ans+=f[p[0]][i]; printf("%lld",ans); } int main(){ freopen("in","r",stdin); n=read(); sieve(n); dp(); }
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; const int N=1005; typedef long long ll; inline int read(){ char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();} return x*f; } int n; int p[N]; bool notp[N]; void sieve(int n){ for(int i=2;i<=n;i++){ if(!notp[i]) p[++p[0]]=i; for(int j=1;j<=p[0]&&i*p[j]<=n;j++){ notp[i*p[j]]=1; if(i%p[j]==0) break; } } } ll f[N][N]; void dp(){ f[0][0]=1; for(int i=1;i<=n;i++) f[0][i]=1; for(int i=1;i<=p[0];i++) for(int j=0;j<=n;j++){ f[i][j]=f[i-1][j]; for(int k=p[i];k<=j;k*=p[i]) f[i][j]+=f[i-1][j-k]; } printf("%lld",f[p[0]][n]); } int main(){ freopen("in","r",stdin); n=read(); sieve(n); dp(); }
Copyright:http://www.cnblogs.com/candy99/