• The 2020 ICPC Asia Macau Regional Contest G. Game on Sequence(博弈/好题)


    Grammy is playing a game with her roommate Alice on a sequence A with n non-negative integers 1,2,…,A1,A2,…,An. The rules of the game are described as follows.

    1. They play the game by moving the single token on the sequence, initially the token is at position k.
    2. Grammy takes the first move, and they take moves alternatively.
    3. In any move with the token at position i, the current player must move the token to the next position j such that >j>i and Aj differs from Ai on at most one bit in binary representation.
    4. The player who can't make any legal move loses the game.

    They play this game many times and the sequence can be modified many times. Grammy wants to ask you for some initial states who will win the game if both play optimally.

    Input

    The first line of input contains 2 integers n and m (1≤,≤2000001≤n,m≤200000), denoting the length of the sequence and the number of operations.

    The second line contains n integers 1,2,…,A1,A2,…,An (0≤≤2550≤Ai≤255), denoting the sequence A.

    The next m lines each contains 2 integers op (1≤≤21≤op≤2) and k, denoting each operation:

    • =1op=1 means a modification on the sequence. Grammy will append an integer k (0≤≤2550≤k≤255) at the end of the sequence so the sequence becomes 1,2,…,+1A1,A2,…,AN+1 where N is the current length of the sequence before modification.
    • =2op=2 means a new game starts with the token at position k (1≤≤1≤k≤N), where N is the current length of the sequence. You need to predict the winner of this game.

    Output

    For each operation with =2op=2, output one line containing "Grammy" if Grammy will win, or "Alice" if Alice will win when they play optimally.

    Example

    input

    Copy

    5 5
    1 2 3 4 5
    1 6
    2 5
    1 7
    2 5
    2 1
    

    output

    Copy

    Alice
    Grammy
    Alice
    

    挺好的一个博弈题,可惜没想出来==

    \(f[i]\)表示token位于i处先手是否必胜。首先不难想到暴力:对于每次1操作直接添加,2操作的话从后往前暴力dp来处理询问。转移方程\(\forall j > i,\ f[i]\ |=!f[j]\)。初始化f[n] = 0(这个n是当前的n)。

    考虑如何进行优化。注意到,对于同一个数A的不同出现位置i、j(设i < j),有如下关系:

    1. 若f[j] = 0,则f[i] = 1(因为先手在i可以选择直接到j,此时后手变成了先手(必败))。
    2. 若f[j] = 1,则f[i] = 1(因为这意味着可以从j跳到一个位置k满足f[k] = 0,那么也可以选择直接从i跳到k)

    综上,对于同一个数A的不同出现位置\(p_1,p_2...p_n\)\(f[p_1]=f[p_2]=...=f[p_{n-1}] = 1\),只有\(f[p_n]\)不确定。

    所以实际上只需要考虑0~255每个数最右侧出现位置的f值即可。代码整体流程就是维护一个数组rmax,rmax[x]表示x这个数当前出现的最右侧的那个位置。此时f数组的定义就需要改变一下,f[i]表示i这个数出现的最右侧位置是否先手必胜,所以f数组的大小仅需开到256。对于每个询问1,直接暴力维护一遍f数组。对于每个询问2,如果输入的k这个位置对应的数的出现的最右侧位置不是k,那么一定先手必胜;如果是的话则根据f值判断这个位置是否先手必胜。关键就在于怎么维护。由之前暴力版本的转移方程可知更新时必须按照位置从大到小进行更新,所以每次维护的时候,需要对0~255这256个数按照最右侧出现位置从大到小排序,这样才能保证dp的正确性。

    举个例子,对于数组3 3 4 4 4 5 6 5,如果询问为op = 2, k = 2;此时初始位置为最右侧的3的位置,先手如果要必胜只能考虑最右侧的4,最右侧的5以及最右侧的6这三个位置(否则如果跳到中间的4或者中间的5,轮到了Alice,此时先手必胜,即Alice必胜)。因此暴力更新是没有问题的。

    #include <iostream>
    #include <cstring>
    #include <vector>
    #include <algorithm>
    using namespace std;
    int n, m, a[400006], f[257], rmax[257];
    bool cmp(int a, int b) {
    	return rmax[a] > rmax[b];
    }
    void process() {
    	memset(f, 1, sizeof(f));
    	vector<int> v;
    	for(int i = 0; i < 256; i++) {
    		if(rmax[i]) {//如果这个数出现过再进行排序 否则没有意义
    			v.push_back(i);
    		}
    	}
    	sort(v.begin(), v.end(), cmp);
    	for(auto x : v) {
    		bool ok = 0;
    		for(int i = 0; i < 8; i++) {
    			int tmp = x ^ (1 << i);
    			if(rmax[tmp] < rmax[x]) continue;
    			ok |= (!f[tmp]);
    		}
    		f[x] = ok;
    	}
    }
    int main() {
    	ios::sync_with_stdio(false);
    	cin.tie(0);
    	cin >> n >> m;
    	for(int i = 1; i <= n; i++) {
    		cin >> a[i];
    		rmax[a[i]] = i;
    	}
    	process();//一定不要忘记在这里也要process
    	for(int i = 1; i <= m; i++) {
    		int op, k;
    		cin >> op >> k;
    		if(op == 1) {
    			n++;
    			a[n] = k;
    			rmax[k] = n;
    			process();//暴力维护
    		} else {
    			if(rmax[a[k]] > k) {
    				puts("Grammy");
    			} else {
    				if(f[a[k]]) {
    					puts("Grammy");
    				} else {
    					puts("Alice");
    				}
    			}
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    (一)Python入门-6面向对象编程:01面向对象和面向过程区别-对象的进化
    (一)Python入门-5函数:10nonlocal-global-LEGB规则
    (一)Python入门-5函数:09嵌套函数(内部函数)-数据隐藏
    (一)Python入门-5函数:08递归函数
    (一)Python入门-5函数:07lambda表达式和匿名函数-eval()函数
    (一)Python入门-5函数:06参数类型-位置参数-默认值参数-命名参数-可变参数-强制命名参数
    (一)Python入门-5函数:05参数的传递-可变对象-不可变对象-浅拷贝和深拷贝-不可变对象含可变子对象
    (一)Python入门-5函数:04变量的作用域-全局变量-局部变量-栈帧内存分析-效率测试
    Java学习第十二章 之 自定义数据类型的使用
    Java学习第十一章 之 final、static、内部类
  • 原文地址:https://www.cnblogs.com/lipoicyclic/p/15526219.html
Copyright © 2020-2023  润新知