码迷,mamicode.com
首页 > 其他好文 > 详细

BZOJ1025 [SCOI2009]游戏

时间:2014-10-18 18:30:53      阅读:239      评论:0      收藏:0      [点我收藏+]

标签:io   os   for   sp   on   问题   log   代码   amp   

题意:对于一些长度为n的排列,将其作为一个置换,那么可能有一个自置换的次数使其回到1,2,3,...,n的情况。求对于所有能够回到1,2,3..,n的排列,不同的次数共有多少种。


思路:我们将置换划分成循环节的形式,那么我们发现最终可能的置换一定是这种形式:

(2,1)(3)(5,6,4)(7)

1,2->2,1

3->3

4,5,6->5,6,4

7->7

并且,若一段的长度为L,那么在第一行下面就是每L行一个循环。最后一行就是我们想要的正序排列。

那么总的行数就是1+LCM(L1,L2,L3,L4,...Lk).

其中k表示划分为k段,并且有L1+L2+L3+...+Lk=n.

于是问题转化为k个正整数,和为n,求他们的最小公倍数的数目。

事实上,k是不确定的,并不容易处理。于是我们转而考虑某个答案ans=p1^a1*p2^a2*..*pm^am是否是n的一个可行答案。


我们有以下结论:若p1^a1+p2^a2+...+pm^am<=n,则ans=p1^a1*p2^a2*..*pm^am是n的一个可行答案。

这个现在我只能给出一种比较正确的证明。

利用反证法证明:不妨设n的某个可行答案ans=p1^a1*p2^a2*p3^a3*...*pm^am,且p1^a1+p2^a2+p3^a3+...+pm^am>n.

由于p1^a1在LCM中,那么必然有某个L是p1^a1的倍数。但是有一些L非常牛,可能是多个pi^ai的倍数。


不妨举个小例子:

设L1是p1^a1的倍数,L3是p2^a2*p3^a3*p4^a4的倍数(L2呢?打酱油去啦~~),L4是p5^a5的倍数,L5是p6^a6*p7^a7的倍数,剩下的L都打酱油去了。

然后显然有L1+L3+L4+L5=b1*p1^a1+b2*p2^a2*p3^a3*p4^a4+b3*p5^a5+b4*p6^a6*p7^a7<=n(因为有一些L打酱油去了)

这里有b1,b2,b3,b4为正整数。

而又有p1^a1+p2^a2+p3^a3+...+p7^a7>n,那么由于都是大于1的数,我们可以认为在每一段内都有乘积比和要大,再乘以一个倍数,必然也比和要大,那么就有:

L1+L3+L4+L5=b1*p1^a1+b2*p2^a2*p3^a3*p4^a4+b3*p5^a5+b4*p6^a6*p7^a7>n

产生矛盾!

那么我们就证明了上述结论。


那么现在问题就转化为求有多少种(a1,a2,a3,...am),满足p1^a1+p2^a2+p3^a3+...+pm^am<=n

利用分组背包就可以通过了。

注意边界条件。


代码:

/**
 * Author: wyfcyx
 * Problem: BZOJ1025
 * Keywords: Math, Dynamic Programming
 * Time Complexity: O(N*N/logN*logN)=O(N^2)
 * Created Time: 14-10-18
 **/

#include <cstdio>
#include <cstring>
#include <cctype>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 1000;
int p[N], id;
bool notp[N + 1];
void pre() {
	int i, j;
	for(i = 2; i <= N; ++i) {
		if (!notp[i])
			p[++id] = i;
		for(j = 1; j <= id && i * p[j] <= N; ++j) {
			notp[i * p[j]] = 1;
			if (i % p[j] == 0)
				break;
		}
	}
}

unsigned long long f[200][1001];

int main() {
	int n;
	scanf("%d", &n);
	
	pre();
	
	register int i, j, k;
	for(i = 0; i <= id; ++i)
		f[i][0] = 1;
	for(j = 1; j <= n; ++j)
		f[0][j] = 1;
	for(i = 1; i <= id; ++i) {
		for(j = 1; j <= n; ++j) {
			f[i][j] = f[i - 1][j];
			for(k = p[i]; k <= j; k *= p[i])
				f[i][j] += f[i - 1][j - k];
		}
	}
	printf("%llu", f[id][n]);
	
	return 0;
}


BZOJ1025 [SCOI2009]游戏

标签:io   os   for   sp   on   问题   log   代码   amp   

原文地址:http://blog.csdn.net/wyfcyx_forever/article/details/40211739

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!