每天一道博弈论之“E&D”

题意:

给你n组石子,每组有两摞。操作为选其中一摞石子分成两摞,抛弃原来同一组的另一摞石子,直到无法操作。问给你这n组石子,问先手胜还是后手胜。 (比如(10,8,),你可以将10分为(5,5),那么这一组石子就成了(5,5),那个8就可以不用管了)

题目链接:https://www.luogu.org/problemnew/show/P2148

题解:

其实这道题我也不是很会,看了题解才写出来T_T

首先每组是独立的,可以求出每一组的SG值然后用SG定理解决。

那么每组的SG值怎么求呢?

  好像没有人会qwq 所以我们先打个表。比如下面这个10*10的表(第一行为列号)

i:0 1 2 3 4 5 6 7 8 9 10
i:1 0 1 0 2 0 1 0 3 0 1
i:2 1 1 2 2 1 1 3 3 1 1
i:3 0 2 0 2 0 3 0 3 0 2
i:4 2 2 2 2 3 3 3 3 2 2
i:5 0 1 0 3 0 1 0 3 0 1
i:6 1 1 3 3 1 1 3 3 1 1
i:7 0 3 0 3 0 3 0 3 0 4
i:8 3 3 3 3 3 3 3 3 4 4
i:9 0 1 0 2 0 1 0 4 0 1
i:10 1 1 2 2 1 1 4 4 1 1

  我(bie)们(ren)可以发现这么一个规律:

  1,当i和j都是奇数时sg值为0 .  2,当i和j都为偶数是sg[i][j] = sg[i/2][j/2]+1

  3,当i,j一奇一偶时(假设i为奇数)sg[i][j] = sg[i+1][j+1]

  复杂度为O(t*n*logS)

  于是这道题就做完啦qwq

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
#define RI register int
using namespace std;
const int INF = 0x7ffffff ;
const int N = 1e4 + 10 ;

inline int read()
{
    int k = 0 , f = 1 ; char c = getchar() ;
    for( ; !isdigit(c) ; c = getchar())
      if(c == ‘-‘) f = -1 ;
    for( ; isdigit(c) ; c = getchar())
      k = k*10 + c-‘0‘ ;
    return k*f ;
}

int n, hh[N] ;

int ser(int x,int y) {
    if(x%2 && y%2) return 0 ;
    if(x%2 &&!(y%2)) return ser(x+1,y) ;
    if(!(x%2) && y%2) return ser(x,y+1) ;
    return ser(x>>1,y>>1)+1 ;
}

int main() {
    int t = read() ;
    while(t--) {
        int cnt = 0 ;
        n = read() ;
        n >>= 1 ;
        for(int i=1;i<=n;i++) {
            int a = read(), b = read() ;
            hh[++cnt] = ser(a,b) ;
        }
        int ans = 0 ;
        for(int i=1;i<=cnt;i++) ans ^= hh[i] ;
        if(ans) printf("YES\n") ;
        else printf("NO\n") ;
    }
    return 0 ;

}

打表代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
#define RI register int
using namespace std;
const int INF = 0x7ffffff ;
const int N = 1000 + 10 ;

inline int read()
{
    int k = 0 , f = 1 ; char c = getchar() ;
    for( ; !isdigit(c) ; c = getchar())
      if(c == ‘-‘) f = -1 ;
    for( ; isdigit(c) ; c = getchar())
      k = k*10 + c-‘0‘ ;
    return k*f ;
}

int sg[N][N] ; 

int ser(int x,int y) {
    if(sg[x][y]) return sg[x][y] ;
    bool vis[N] = {0} ;
    for(int i=1;i<x;i++) vis[ser(x-i,i)] = 1 ;
    for(int i=1;i<y;i++) vis[ser(y-i,i)] = 1 ;
    for(int i=0; ;i++)
      if(!vis[i]) {
          sg[x][y] = i ; return i ;
      }
}

int main() {
    sg[1][1] = 0 ;
    int n = read() ;
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=n;j++) {
            sg[i][j] = ser(i,j) ;
        }
    }

    printf("i:0  ") ;
    for(int i=1;i<=n;i++) if(i<10) printf("%d ",i) ; else printf("%d",i) ; printf("\n") ; // 输出列号 

    for(int i=1;i<=n;i++) {
        if(i<10) printf("i:%d  ",i) ; // 输出行号
        else printf("i:%d ",i) ; // 输出行号(为了整齐
        for(int j=1;j<=n;j++) {
            printf("%d ",sg[i][j]) ;
        }
        printf("\n") ;
    }
    return 0 ;
}

原文地址:https://www.cnblogs.com/zub23333/p/8508246.html

时间: 2024-12-09 15:59:05

每天一道博弈论之“E&D”的相关文章

每天一道博弈论之“逆序对游戏”

题目链接:https://loj.ac/problem/524 题意: 一开始有一个长度为n的序列,其中有的数是已知的(这些已知数两两不同),有些数是待填写的.操作为任选一个代填写位置填写一个未在序列中出现过的实数.无法填写时游戏结束.当游戏结束后若逆序对数目为奇数,则先手胜,否则后手胜. 题解: 其实有一个性质:若原序列逆序对数为奇数,你一定可以任选一个位置填写一个数使得逆序对数为偶数.反之亦然.(因为序列中的数是不重复的)尽量自己感性理解吧,十分严格的证明我也不会 所以若存在代填位置且为奇数

每天一道博弈论之“牛的数字游戏”

题意: 给你一个数n(n<=1e6),玩家可以进行的操作为减去该数最大数码或最小非零数码.即数2014可以减去1变成2013或减去4变成2010.将数变成0的一方赢. 题解: 直接求出1-1e6的SG函数值即可.复杂度O(n*lgn)(这里的log以10为底). #include<iostream> #include<cstdio> #include<cstring> #define LL long long #define RI register int usi

每天一道博弈论之“A funny game”(poj 2484)

题意: n枚硬币排成一个环,操作为可以选择相邻的两个取走(相邻指的是最开始相邻,即不会自动补成环).问先手胜还是后手胜. 题解: 首先我们考虑1和2,则明显是先手必胜. 如果大于等于3,那么先手取后一定是一条链.这时我们一定可以把这一条链变成两条相等的链.(如果链长是奇数就取掉最中间的那个,否则取掉最中间的两个).那么一个游戏就被分成了两个相同的独立游戏.那么假如对方在某一游戏中进行了一种操作,我们只要在另一游戏中重复该操作即可.那么可以保证我们面临的两个游戏状态一定相同,而对方面临的一定不同.

转|poj2234maches game|博弈论

以下内容转自精灵的博客.时间限制:     1000ms 内存限制:     65536kB 描述    Here is a simple game. In this game, there are several piles of matches and two players. The two player play in turn. In each turn, one can choose a pile and take away arbitrary number of matches f

「POJ2505」A multiplication game [博弈论]

题目链接:http://poj.org/problem?id=2505 题目大意: 两个人轮流玩游戏,Stan先手,数字 p从1开始,Stan乘以一个2-9的数,然后Ollie再乘以一个2-9的数,直到谁先将p乘到p>=n时那个人就赢了,而且轮到某人时,某人必须乘以2-9的一个数. 解题思路: 这是一道博弈论的题目.不过这道题并没有用SG函数相关的知识.首先我们可以很快判断区间[2,9]必定是先手胜然后紧接着是区间[10,18]区间是后手胜然后是什么呢?[18,??]我们可以这样来理解:我们可以

一点对博弈论的理解

大家对博弈论最深的理解相比就是带有规律性的石子游戏 可这些是前辈们多年总结起来的 在面对一道博弈论的题目时怎么发现规律或在没有规律时表示状态的博弈状态呢? 引入\(SG\)函数 \(N\)表示必胜状态,用非零自然数表示:\(P\)表示必败状态,用零表示 我们定义一下这两种状态的转移(定义之类的): 所有的终止状态都为\(P\)状态 对于任意的\(N\)状态,存在至少一条路径可以转移到\(P\)状态 对于任意的\(P\)状态,只能转移到\(N\)状态 我们通常从最终状态向前推,则一个状态由多个状态

codeforces #330 div2

A: #include<cstdio> #include<algorithm> #include<cmath> #include<map> #include<iostream> #include<vector> #include<cstring> #include<queue> #include<string> using namespace std; int n,m; int ans; int a

hihocoder 1154 Spring Outing

传送门 #1154 : Spring Outing 时间限制:20000ms 单点时限:1000ms 内存限制:256MB 描述 You class are planning for a spring outing. N people are voting for a destination out of K candidate places. The voting progress is below: First the class vote for the first candidate p

第六届山东省赛总结贴

省赛结束快两个星期辣,原谅弱到现在才写总结,拖延症晚期路过(逃. 其实觉得自己挺幸运的,省赛前因为各种原因和FF and +才组队,然后瞬间抱上大腿,完美蜕变,成为一名辅助,走上人生巅峰. 一群不会起名字的BD,在FF偶尔听到的一首歌里我们选了呢个乐队的名字Dreamtale(梦境传说,瞬间像磕了药一样,精神抖擞.当时距离省赛还有两个月不到的时间,为了磨合也是锻炼,每周四下午都要打一场比赛,加上周六的比赛,一周双开.其实初学当一名辅助也是很坎坷的,开始周四做的是往年省赛的题目,初学辅助有的细节方