E - Lunch(博弈论,推sg函数)

https://vjudge.net/problem/HDU-6892

题意:
给了n堆石子(蛋糕),每个石子的个数为ai,可以将石子分为k份,其中ai%k==0,没得分的人算失败,问先手必胜还是必败

分析:
典型的博弈论问题,先分析一下每个石子的sg函数,首先很明显的,质数的sg函数值都是1,对于非质数,拿6举例,可以这么分,分为6个1,分为3个2或者分为2个3这三种状态,每种状态的sg值其实就是他们各自的sg值的异或和,比如2 2 2 这个状态,因为sg(2)=1,所以三个2的异或值为1。所以sg(6)=mex{0,1}=2。

但是ai的范围是1e9。如果像这样正常求sg值肯定会超时,考虑别的方法。

直接打表找规律。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6;
int f[N];
int sg(int x)
{
    if (f[x] != -1)
        return f[x];
    set<int> S;
    vector<int> v;
    for (int i = 2; i < x; ++i) // 找这个数的因子
        if (x % i == 0)
            v.push_back(i);
    if (v.size() == 0) // x是质数,sg值是1
        return 1;
    S.insert(0); // 每个数都能被他本身整除,分成n个1,n个1的sg值是0
    for (int i = 0; i < v.size(); ++i)
    {
        if ((x / v[i]) % 2 == 0) // 对于这个因子,如果能分解的份数是偶数,那么异或和为0
            S.insert(0);
        else // 否则为v[i]的sg值
            S.insert(sg(v[i]));
    }
    for (int i = 0;; ++i) // 求mex
        if (!S.count(i))
            return f[x] = i;
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    memset(f, -1, sizeof(f));
    f[1] = 0;
    for (int i = 1; i <= 300; i++)
    {
        cout << i << ' ' << sg(i) << "  ";
    }
}

不难想到sg的值应该跟质数有很大关系,对应表中的数据,我们对小于30的非质数都进行质因数分解:

不难发现这样一个规律:如果这个数的质因子没有2,那么sg(n)=对n质因数分解后得到的质因子的个数;如果有2,则sg(n)++;

根据这个规律我们能很快得到sg(ai)的值了,先进行质数筛(质数筛只需要筛到sqrt(1e9)就行了),然后对每个ai进行质因子分解求出sg(ai),然后求异或和判断结果是否为0就行了:
 

#include <bits/stdc++.h>
using namespace std;
// #define int long long
const int N = 1E5 + 10;
bool st[N];
int v[N];
int idx;

void init()
{
	for (int i = 2; i < N; i++)
	{
		if (st[i])
			continue;
		for (int j = i + i; j < N; j += i)
		{
			st[j] = true;
		}
		v[idx] = i;
		idx++;
	}
}

void solve()
{
	int n;
	scanf("%d", &n);
	int res = 0;
	for (int k = 0; k < n; k++)
	{
		int x;
		scanf("%d", &x);
		int sg = 0;

		if (x % 2 == 0)
			sg++;
		while (x % 2 == 0)
		{
			x /= 2;
		}
		for (int i = 1; i < idx; i++)
		{
			while (x % v[i] == 0)
			{
				sg++;
				x /= v[i];
			}
			long long t = v[i];
			t *= t;
			if (t > (long long)x) // 这里加个提前跳出的条件,否则会超时
				break;
		}
		if (x != 1) // x如果不为1,说明跳出过早,导致最后一个没有算(比如说5进去之后 3*3>5直接跳出了,所以要补上)
			sg++;
		if (k == 0)
			res = sg;
		else
			res ^= sg;
	}
	printf("%c\n", res ? 'W' : 'L');
	return;
}
signed main()
{
	// ios::sync_with_stdio(false);
	// cin.tie(0);
	// cout.tie(0);
	init();
	int t;
	cin >> t;
	while (t--)
		solve();
}

需要注意的是这道题的时间卡的很死,define int long long 会超时,用cin cout也会超时,质因数分解不加优化也会超时,最后刚好2000多ms过

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值