SRM565 DIV1 LEVEL2 TheDivisionGame

本文介绍了一道TopCoder题目,通过博弈论解决数字质因数计数问题。利用质因数分解技巧和动态规划算法,高效计算区间内数字的质因数数量,并求出获胜情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  http://community.topcoder.com/stat?c=problem_statement&pm=12264

  这道题目的意思就是有一堆数字,每次操作可以这样做,从中选出一个大于1的数字a,然后使用这个数任意一个大于1的因子b去除以这个数,及 a / b,然后使用结果来替换a。这样两个人轮流操作,谁不能操作了就算输了。

  这题很有博弈的味道。如果对于博弈不熟悉的话,可以看看下面的这篇文章,写的很详细

  http://blog.youkuaiyun.com/acm_cxlove/article/details/7854530

   如果看了上面的那篇文章,文章中有提到这样的一种博弈,有n堆石子,每次操作我们可以拿其中一堆中的任意个,及可以拿一个,或者多个,或者一次都拿完。其实这个和我们这题很相似,对于我们挑选的一个数字,这个数字有多杀个质因子,就类似这堆石子有多少个。如12 = 2 * 2 * 3,有三个质因子,那么我们就能把这个数字看作是有3颗石子的堆。现在假设我们知道了每堆石子,及每个数有多少个质因子,那么我就能求求出答案了。现在我们就要求出从L到R,这几个数每一个数有多少个质因子。


  1、首先筛选出sqrt(R) + 1中有哪些素数

  2、计算每一个从L到R的数有多少个质因子。我刚开始的时候,是从L到R,每一个都数都单独的求。及对于其中的一个数a,我使用上面求好的素数数列一个一个验证过去是不是a的质因子,如果是的话有几个。但是这样求的话,效率很不好,超时了。后面我看了别人的答案,我看到了种更好的方法。我的这个朴素的方法中,我是一个一个素数验证过去的,所以有很多不是a的因子的,我也要验证一一下,这样就会浪费很多的效率。另外一个方法就是我们使用素数去主动的查看L到R中的数中有多少个这样的素数。看下面的代码会更加的清楚

for (int i=L ; i<=R ; i++) {
				b[i - L] = i;
			} 
			memset(a , 0 , sizeof(a));

			for (int i=0 ; i<prime_len ; i++) {
				int temp = primes[i];
				for (int j=((L - 1) / temp + 1) * temp ; j<= R ; j += temp) {//attention!!!
					while (b[j - L] % temp == 0) {
						b[j - L] /= temp;
						a[j - L]++;
					}
				}
			}
			for (int i=L ; i<=R ; i++) {
				if (b[i - L] != 1) a[i - L]++;
			}


比如其中的一个素数2,从大于等于L的第一个2的倍数开始计算,计算每一个数有多少个2的因子。因为j += temp,所以我们每次去查找的话,都是有效的,及每一个j都是temp的倍数,这样就比我上面一个一个素数尝试过去快很多。

这样求好之后,后面就是一个简单的dp了,因为数字1001000000 < 2 ^ 31,每一个数最多也只有31个因子。ans[2][32], if (ans[u][j] != 0) ans[p][j ^ a[i - L] ] += ans[u][j]。然后我们统计全部不为and[u][j]且j不为的值就可以了。

下面是代码:

public:
        long long countWinningIntervals(int L, int R)
        {
			initPrime(((int)(sqrt(R * 1.0))) + 1); //attention!!!
			LL ret = 0;
            for (int i=L ; i<=R ; i++) {
				b[i - L] = i;
			} 
			memset(a , 0 , sizeof(a));

			for (int i=0 ; i<prime_len ; i++) {
				int temp = primes[i];
				for (int j=((L - 1) / temp + 1) * temp ; j<= R ; j += temp) {//attention!!!
					while (b[j - L] % temp == 0) {
						b[j - L] /= temp;
						a[j - L]++;
					}
				}
			}
			for (int i=L ; i<=R ; i++) {
				if (b[i - L] != 1) a[i - L]++;
			}

			memset(ans , 0 , sizeof(ans));

			int p = 0;
			int u = 1;
			ans[u][a[0]] = 1;
			ret = 1;

			for (int i= L + 1 ; i<=R ; i++) {
				memset(ans[p] , 0 , sizeof(ans[p]));
				for (int j=0 ; j<=31 ; j++) {
					if (ans[u][j] != 0) {
						int temp = j ^ a[i - L];
						ans[p][temp] += ans[u][j];
						if (temp != 0)
							ret += ans[u][j];
					}
				}
				ans[p][a[i - L]]++;
				ret++;
				p = 1 - p;
				u = 1 - u;
			}
			return ret;
		}

		void initPrime(int n) {
			memset(gash , 0 , sizeof(gash));
			prime_len = 0;

			for (int i=2 ; i<=n ; i++) {
				if (!gash[i]) {
					primes[prime_len++] = i;
				}

				for (int j=0 ; j<prime_len ; j++) {
					int temp = i * primes[j];
					if (temp > n) break;
					gash[temp] = true;
					if (i % primes[j] == 0) break;
				}
			}
		}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值