(G. Remove the Prime)[https://codeforces.com/gym/103260/problem/G]

tag:

  • cf2000
  • 博弈,Pollard-Rho

题意:

对于连续序列,两个人执行操作,对[l,r]区间指定一个被这些数字都可以整除的质数,使得区间内所有数字被该素数除至不能再除
重复操作至所有数字变为1
先后手均使用最优解,求谁赢。

题解:

首先是考虑所有数字均做一次pollard-rho,求所有数字的质因数序列。 然后求所有质数的连续操作序列,记录长度。

此时我们就会得到一个小小的博弈状态。对于每个人都可以通过[l,r]操作变成下一个状态。这有点类似nimu博弈。

我们将状态列出来,得到当操作长度为kk时,若我们从中间抓取,可能会拆成两个数字,但是根据nimu博弈规则,若有两个石子堆均满足个数为n,那么只要操作镜像就可以实现后手胜利。因此,这里的操作拆成了两个数字,并不影响尼姆的结论,后手胜利当且仅当存在镜像操作可以从头做到尾。

因此结果就是nimu和是否为0

代码:

#include<bits/stdc++.h>
#include<map>
#include<random>
using namespace std;
#define ll long long
mt19937 rd((ll)(new int));
typedef __int128 lll;

// const int test_int[]={2,7,61};
ll gcd(ll a, ll b)
{
	return b == 0 ? a : gcd(b, a % b);
}


#define __gcd gcd


const ll test[] = { 2,325,9375,28178,450775,9780504,1795265022 };
ll qpow(ll x, ll n, ll mod) {
	ll c = 1;
	for (x %= mod; n; n /= 2, x = (lll)x * x % mod)
		if (n & 1) c = (lll)c * x % mod;
	return c;
}
int miller_robin(ll n) {
	if (n < 2 || n % 6 % 4 != 1) return (n | 1) == 3;
	ll q = __builtin_ctzll(n - 1), m = (n - 1) >> q;
	for (ll a : test) {
		if (a >= n) break;
		ll x = qpow(a, m, n), x1, i = q;
		for (; i-- && x != 1; x = x1) {
			x1 = (lll)x * x % n;
			if (x1 == 1 && x != 1 && x != n - 1) return 0;
		}
		if (x != 1) return 0;
	}
	return 1;
}
map<ll, ll> ans;
ll pollard_rho(ll x) {
	ll s = 0, t = 0, c = rd() % (x - 1) + 1, d = 1;
	for (ll val, step, g = 1; d == 1; g <<= 1, s = t) {
		val = 1;
		for (step = 1; d == 1 && step <= g; step++) {
			t = ((lll)t * t + c) % x;
			val = (lll)val * abs(t - s) % x;
			if (step % 100 == 0) d = __gcd(val, x);
		}
		d = __gcd(val, x);
	}
	return d;
}
void fac(ll n) {
	if (n < 2) return;
	if (miller_robin(n)) {
		ans[n]++; return;
	}
	ll p = n;
	while (p >= n) p = pollard_rho(n);
	fac(n / p), fac(p);
}
const int maxn = 1e4 + 5;
ll x[maxn];
vector<int>ve[2000];
vector<int>res;
void solve() {
	ll n;
	scanf("%lld", &n);

	for (int i = 1; i <= n; i++) {
		cin >> x[i];
		//if (miller_robin(x[i]))ve[i].push_back(x[i]);
		ans.clear();
		fac(x[i]);
		for (auto k : ans) {
			ve[i].push_back(k.first);
		}
	}
	ll cnt = 0;

	for (int i = 1; i <= n; i++) {
		for (auto k : ve[i]) {
			ll f = 1;
			for (int j = i + 1; j <= n; j++) {
				auto tmp = lower_bound(ve[j].begin(), ve[j].end(), k);
				if (*tmp == k) {
					f++;
					ve[j].erase(tmp);
				}
				else {
					break;
				}
			}
			cnt ^= f;
			res.push_back(f);
		}
	}
	if (cnt)cout << "First\n";
	else cout << "Second\n";
	//if (miller_robin(n)) return void(puts("Prime"));
	//ans.clear();
	//fac(n);
	//printf("%lld\n", ans.rbegin()->first);
	// for(auto [l,r]:ans) printf("%lld %lld\n",l,r);
}
int main(){
    solve();
}