SCOI2009 游戏

Description:

windy学会了一种游戏。

对于1到N这N个数字,都有唯一且不同的1到N的数字与之对应。

最开始windy把数字按顺序1,2,3,……,N写一排在纸上。

然后再在这一排下面写上它们对应的数字。

然后又在新的一排下面写上它们对应的数字。

如此反复,直到序列再次变为1,2,3,……,N。

如: 1 2 3 4 5 6

对应的关系为

1->2 2->3 3->1 4->5 5->4 6->6

windy的操作如下

1 2 3 4 5 6

2 3 1 5 4 6

3 1 2 4 5 6

1 2 3 5 4 6

2 3 1 4 5 6

3 1 2 5 4 6

1 2 3 4 5 6

这时,我们就有若干排1到N的排列,上例中有7排。

现在windy想知道,对于所有可能的对应关系,有多少种可能的排数。

Hint:

n<=1000

Solution:

所谓的对应关系就是置换。

开始想到置换群,发现不能直接转化。

思考为什么会重复回来,因为,向每一个置换的位置连一条有向边,

n个点n条边,并且每个点必然一个入度一个出度,所以一定连出来若干个环。

发现,环长就是这个环上的点重复一次的排数

所以,排数就是lcm(L1,L2,L3,L4...Lk)+1了。

而由于∑Li=n,所以,所有的L其实就是n的一个拆分。

那么答案就是,n的所有正整数拆分中,拆分的所有数的不同的lcm有几个。

但是n的拆分太多太多了。。。这种算法也就算到n=50就不行了。

无法继续考虑。

正难则反。

考虑凑出多少个不同的lcm不行的话,

就钦定一个数m是lcm,看能不能拼出来。

m显然无法枚举。那就考虑m作为lcm是怎么来的。

我们把m质因数分解一下,

m=p1^q1*p2^q2*...pk^qk

那么,由于lcm是所有的数中质因数分解的所有质数指数取一个max,

这些原来的拆分的数中,分别质因数分解,质数指数的max就分别是:q1,q2...qk

所以,只要判断p1^q1+p2^q2+...+pk^qk<=n是否成立即可。

若成立,那么m就是一个合法可以拼凑的lcm

(相当于,我们只取这些质数的最大指数,作和,剩下的全部取1。

反正对于lcm=m来讲,取其他的数,也都是被max埋没了。)

我们的问题就转化成:p1^a1+p2^a2+..+pl^al<=n的解的个数。(对于每一个不同的a的选择,就对应一种lcm)

(其中,pl就是最大的小于n的质数)

然后就是多重背包啦!

物品上限,只要pk^ak不要超过n即可,基本没有几件。

复杂度:O(N^2)左右

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1000+5;
int pri[N],tot;
ll f[N];
bool vis[N];
int n;
void sieve(){
	for(int i=2;i<=n;i++){
		if(!vis[i]){
			pri[++tot]=i;
		}
		for(int j=1;j<=tot;j++){
			if(pri[j]*i>n) break;
			vis[pri[j]*i]=1;
			if(i%pri[j]==0) break;
		}
	}
}
int main()
{
	scanf("%d",&n);
	sieve();
	f[0]=1;
	for(int i=1;i<=tot;i++)
	 for(int j=n;j>=0;j--){
	 	for(int k=pri[i];k<=j;k*=pri[i]){
	 		f[j]+=f[j-k];
		 }
	 }
	ll ans=0;
	for(int i=0;i<=n;i++)ans+=f[i];
	printf("%lld",ans);
	return 0;
}

总结:

还是正难则反没有想到啊。之前都想到了。

卡在了自然数拆分的方案怎么统计。。。

有时候,直接求拼凑的方案等无法处理,

就考虑反着来,这个方案能否被拼凑。

二分答案本质就是这个思路。

相当于,我不知道答案求答案,和我猜一个答案验证一下。

原文地址:https://www.cnblogs.com/Miracevin/p/9419195.html