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过