bzoj1025 [SCOI2009]游戏

bzoj1025 [SCOI2009]游戏


原题链接


题解

几个数会出现环。
出现了环就会出现循环。
一个环循环次数为数字个数。
总循环次数为各个循环次数的最小公倍数。
所有环循环次数=\(\sum\)各个环循环次数。
问题转化为:将N任意分成任意个数,每个数\(\in[1,N]\)
求最小公倍数多少种可能???
很难对吧
~~设f[i][j],将i分成若干1j的数,最小公倍数多少种可能。一堆重复搞死你~
但如果全是质数and1就好些辣!
然鹅再想想。。。

把N分成互质的数,肯定能涵盖所有答案
比如

\[N=a+b, (a,b)=c\neq1 , [a,b]=d \]

\[N'=\frac{a}{c}+\frac{b}{c}+c<N,[\frac{a}{c},\frac{b}{c},c]=d \]

\(N'<N\)要凑出\(N\)只需要再塞几个1。
所以分成互质的数就行了

既然是互质的数,就只能是质数的幂and1
设f[i][j]前j个质数的次幂和1拼出i的lim数
转移只需要枚举凑prime[j]的几次方
既然上面说塞几个1就行了,那答案是 \(\sum_{i=0}^{n}f[n][cnt]\)
cnt是[2,n]区间内质数数


Code

// It is made by XZZ
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cctype>
using namespace std;
#define rep(a,b,c) for(rg int a=b;a<=c;a++)
#define drep(a,b,c) for(rg int a=b;a>=c;a--)
#define erep(a,b) for(rg int a=fir[b];a;a=nxt[a])
#define il inline
#define rg register
#define vd void
typedef long long ll;
il int gi(){
    rg int x=0;rg char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return x;
}
il vd work(int n=gi()){
    int prime[170];bool t[1010]={0};
    ll f[1010][170]={0};
    int cnt=0;
    rep(i,2,n){
	if(!t[i]){
	    prime[++cnt]=i;
	    for(int j=i;j<=n;j+=i)t[j]=1;
	}
    }
    f[0][0]=1;
    rep(j,1,cnt)rep(i,0,n){
	f[i][j]=f[i][j-1];
	for(rg int k=prime[j];k<=i;k*=prime[j])f[i][j]+=f[i-k][j-1];
    }
    ll ans=0;
    rep(i,0,n)ans+=f[i][cnt];
    printf("%lld\n",ans);
}
int main(){
    int n;
    while(scanf("%d",&n)==1)work(n);
    return 0;
}

然鹅,有一个优化————滚动数组
然鹅,还有个优化————记忆搜
然鹅,什么优化都比不上这个————打表!!!
打表代码太大放这里

PS.窝真蒻。。。这题都没想出来
posted @ 2017-09-08 09:53  菜狗xzz  阅读(101)  评论(0编辑  收藏  举报