HAOI2015 数组游戏

P3179 [HAOI2015]数组游戏 [* medium]

给定 \(n\) 个硬币,多次查询,每次给定 \(w\) 表示有 \(w\) 枚硬币正面朝上(具体位置给定),每次操作为:

  1. 选择一面正面朝上的硬币并翻转,然后假设其下标为 \(x\),此时你可以翻转 \(x,2x...kx(kx\le n)\)

求先手后手谁会 win,\(n\le 10^9,q,w\le 100\)

Solution

首先有一个我不会的结论,但是论证并不是很难,若干枚硬币组成的游戏的 SG 值就是各个硬币的 SG 值的异或和。

论证的话考虑翻转一枚硬币并当作子结论处理,由于翻硬币中消去一个元素与对游戏的 SG 值异或上相同的 SG 值等价,所以不难看出 SG 定理的成立。

知道这个后这个题就很 easy 了。

不难注意到,对于 \(x\) 这枚硬币而言,我们计算的 SG 值等价于将 \(x\) 视为 \(1\) 号硬币,然后考虑总共有 \(\lfloor\frac{n}{x}\rfloor\) 枚硬币下的情况。

考虑对于某个 \(\lfloor\frac{n}{x}\rfloor\) 如何快速的计算答案,等价于将此情况下 \(2,3...m\) 的 SG 值异或起来的 \(\rm mex\)

由于结论仍然是生效的,此时只需要考虑 \(\frac{n}{xt}\) 的形式,从小到大枚举 \(\frac{n}{xt}\) 并根据奇偶性判断能否对后续产生影响(加入肯定是可以加入的),对于每次我们计算一次 \(\rm mex\),不难注意到 \(\rm mex\) 的上界是 \(\sqrt{\frac{n}{x}}\) 的级别,所以直接暴力找 mex 就可以了。

于是我们可以在 \(\mathcal O(n^{\frac{3}{4}})\) 的时间复杂度解决这个问题。

  • \(\sum \sqrt{\frac{n}{x}}\le \int \sqrt{x}+\int \sqrt{\frac{n}{x}}=n^{\frac{3}{4}}\)

\(Code:\)

#include<bits/stdc++.h>
using namespace std ;
#define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define re register
int gi() {
	char cc = getchar() ; int cn = 0, flus = 1 ;
	while( cc < '0' || cc > '9' ) {  if( cc == '-' ) flus = - flus ; cc = getchar() ; }
	while( cc >= '0' && cc <= '9' )  cn = cn * 10 + cc - '0', cc = getchar() ;
	return cn * flus ;
}
const int N = 1e5 + 5 ; 
int n, cnt, top, num, v1[N], v2[N], sg[N], f[N], g[N], mx[N] ; 
struct node {
	int len, w ; 
	node(int _len = 0, int _w = 0) { len = _len, w = _w ; }
} st[N] ;
int Get(int x) { return (x <= cnt) ? f[x] : g[n / x] ; }
void add(int x) { mx[x] = 1, sg[++ num] = x ; }
void dec(int x) { mx[x] = 0 ; }
void dfs(int x) {
	if( x <= cnt && v1[x] ) return ; 
	if( x > cnt && v2[n / x] ) return ; 
	(x <= cnt) ? v1[x] = 1 : v2[n / x] = 1 ; 
	for(re int l = 2, r; l <= x; l = r + 1) 
		r = x / (x / l), dfs(x / l) ;
	top = 0 ; 
	for(re int l = 2, r; l <= x; l = r + 1) 
		r = x / (x / l), st[++ top] = node((r - l + 1), Get(x / l)) ;
	int sf = 0, u = 1 ; 
	for(re int j = 1; j <= top; ++ j) {
		add(sf ^ st[j].w) ;
		if( st[j].len & 1 ) sf ^= st[j].w ; 
	} 
	while( mx[u] ) ++ u ; 
	(x <= cnt) ? f[x] = u : g[n / x] = u ;
	while( num ) dec(sg[num]), -- num ; 
}
signed main()
{
	n = gi(), cnt = sqrt(n) ; 
	f[1] = 1, v1[1] = 1, dfs(n) ; 
	int q = gi() ; 
	while( q-- ) {
		int w = gi(), SG = 0, x ; 
		while( w-- ) x = gi(), SG ^= Get(n / x) ; 
		( !SG ) ? puts("No") : puts("Yes") ; 
	}
	return 0 ;
}
posted @ 2020-10-22 23:06  Soulist  阅读(154)  评论(0编辑  收藏  举报