do_while_true

一言(ヒトコト)

「题解」AGC010D Decrementing

序列中至少有一个奇数,否则就会被 \(\gcd\) 除掉了。

如果序列出现了 \(1\),说明无论怎样操作之后 \(\gcd\) 都只能为 \(1\) 了,那么判断偶数个数的奇偶性,若有奇数个偶数那么先手必胜,判掉这个,现在所有的数都是可以被钦点的了。

猜个结论,如果有奇数个偶数,那么先手必胜。

注意到如果钦点一个数 \(-1\) 之后,剩余既有奇数又有偶数,则 \(\gcd\) 为奇数,除掉 \(\gcd\) 之后各个数的奇偶性不变。

那么先手只需要钦点使得给后手 \(>1\) 个奇数,\(>0\) 个偶数,这样后手无论怎么钦点,轮到先手的时候依然是奇数个偶数。

那么特殊情况就是 \(n=2\) 的时候,这个稍微讨论一下就差不多得了发现依然满足结论。

然后看如果先手有偶数个偶数,考虑奇数的个数,如果奇数的个数 \(>1\),那先手不管怎么钦点,轮给后手的都是"奇数个偶数"的必胜状态,所以此时先手必败。

如果奇数的个数 \(=1\),先手如果钦点偶数的话,轮给后手的就是"奇数个偶数"的必胜状态,所以先手之能钦点奇数,这种情况递归下去继续判即可。

每产生一次递归,所有数至少变为原来的一半,所以总复杂度就是 \(\mathcal{O}(N\log^2A)\)

就嗯塞语法糖

#include<iostream>
#include<cstdio>
template <typename T>
T &read(T &r) {
	r = 0; bool w = 0; char ch = getchar();
	while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
	while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar();
	return r = w ? -r : r;
}
const int N = 100010;
int gcd(int a, int b) { return !b ? a : gcd(b, a % b); }
int n, a[N];
#define Win do{puts(p?"Second":"First");return ;}while(0)
#define Lose do{puts(p?"First":"Second");return ;}while(0)
void check(int p) {
	int ct = 0, g = 0, fl = 0;
	for(int i = 1; i <= n; ++i)
		ct += !(a[i]&1),
		g = a[i]&1 ? gcd(g, a[i]-1) : gcd(g, a[i]),
		fl |= a[i]==1;
	if(ct&1) Win;
	if(fl || n-ct>1) Lose;
	for(int i = 1; i <= n; ++i) a[i] = a[i]&1 ? (a[i]-1)/g : a[i]/g;
	check(p^1);
}
signed main() {
	read(n);
	for(int i = 1; i <= n; ++i) read(a[i]);
	check(0);
	return 0;
}
posted @ 2022-01-02 22:31  do_while_true  阅读(52)  评论(0编辑  收藏  举报