关闭

FZU Problem 2240 Daxia & Suneast's problem(博弈+[单点更新,区间查询]线段树)

标签: acm博弈线段树
426人阅读 评论(0) 收藏 举报
分类:

此文章可以使用目录功能哟↑(点击上方[+])

 FZU Problem 2240 Daxia & Suneast's problem

Accept: 0    Submit: 0
Time Limit: 2000 mSec    Memory Limit : 32768 KB

 Problem Description

daxia和suneast玩起来取石子游戏,现有n堆石子放成一排,每堆石子颗数为a1,a2,...,an.

然后开始m轮游戏,每轮游戏之前,suneast先把第i堆的石子改成x颗,然后双方开始在第j堆到第k堆之间进行取石子游戏.

取石子规则如下:

1. daxia先取,然后双方轮流,每次取的数量不得超过该堆的一半;

2. 当轮到某一方,而其不能取到石子的时候,则判其为负.

 Input

测试包含多组数据.

每组数据第一行为2个整数n(0<n<=100000),m(0<m<=100000).

接下来包含一行,共n个整数ai(0<ai<=1000000000).

接下来包含m行,每行4个整数i,x,j,k(1<=i,j,k<=n,0<x<=1000000000).

 Output

每组数据输出m行,如果为daxia胜输出"daxia",suneast胜则输出"suneast".

 Sample Input

3 3
1 1 1
1 2 1 3
1 3 1 2
2 2 3 3

 Sample Output

daxia
suneast
suneast

 Hint

第一轮改后(2 1 1),区间[1,3],daxia取完第一堆一颗获胜

第二轮改后(3 1 1),区间[1,2],daxia和suneast各在第一堆取一颗后,suneast获胜

第三轮改后(3 2 1),区间[3,3],daxia上来就没得取,suneast获胜

 Problem Idea

解题思路:

【题意】
n堆石子,每堆石子颗数为a1,a2,...,an

m轮游戏,每轮游戏开始前,suneast会把第i堆的石子改成x颗

在第j堆到第k堆之间进行取石子游戏

daxia先取,然后双方轮流,每次取的数量不得超过该堆的一半

不能取时为负

【类型】
博弈+[单点更新,区间查询]线段树

【分析】
令人熟悉的取石子游戏,不知道已经是多少次变型

但总的解法还是相似的

对于这种每堆石子的数量可能很大的情况,一般是无法用sg函数直接求得的

这个时候,我们通常的做法是通过sg函数打表来找到规律
在使用sg函数之前,我们先大致了解一下sg函数(具体的可以自行网上学习):
SG函数表示的是某种局面的状态,它是由mex运算得到的
mex表示最小的不属于这个集合的非负整数,例如mex{0,1,2,4}=3,mex{2,3,5}=0,mex{}=0;
其实mex可以这么理解,0表示必败态,非零表示必胜态,根据定理"能一步到达必败态的点就是必胜态",故只有集合包含0,那么mex得到的数必定非0
根据定理"只能到达必胜态的点是必败态",故若集合中不包含0,那么mex得到的数必定为0
接下来,我们回归正题,suneast换石子数只是改变了某堆的sg值,但并不影响我们利用sg值找规律

以下是打表程序:

const int N = 100;
int sg[N];
bool v[N];
int mex(int x)
{
    int i;
    memset(v,false,sizeof(v));
    for(i=1;i<=x/2;i++)
        v[sg[x-i]]=true;
    for(i=0;;i++)
        if(!v[i])
            return i;
}
int main()
{
    int i;
    sg[1]=0;
    for(i=2;i<N;i++)
        sg[i]=mex(i);
    for(i=1;i<N;i++)
        printf("sg[%d]=%d\n",i,sg[i]);
    return 0;
}
规律表如下:


仔细观察上述规律表,似乎有所发现,凡是相同颜色的sg值均+1递增

例如从1开始,每隔4个,sg值+1;例如从2开始,每隔2个,sg值+1;再例如从3开始,每隔8个,sg值+1;从7开始……

好像这一切跟二进制存在一定联系,于是,我们按照二进制重新打一次表

打表程序:

const int N = 100;
int sg[N];
bool v[N];
char b[10];
int mex(int x)
{
    int i;
    memset(v,false,sizeof(v));
    for(i=1;i<=x/2;i++)
        v[sg[x-i]]=true;
    for(i=0;;i++)
        if(!v[i])
            return i;
}
int main()
{
    int i;
    sg[1]=0;
    for(i=2;i<N;i++)
        sg[i]=mex(i);
    for(i=1;i<N;i++)
    {
        itoa(i,b,2);
        printf("sg[%s]=%d\n",b,sg[i]);
    }
    return 0;
}

规律表:


这次好像有了进一步的发现,一个数n的sg值sg[n]等于n的二进制表示抹去为0的最低位及其后面的1之后的值

例如n=41时,二进制表示为101001,它的为0最低位为倒数第2位,所以抹去"01"之后就是它的sg值,故sg[41]=1010(2)=10

好了,到此为止,我们已经会算每一堆石子的sg值

要在第j堆到第k堆之间进行取石子游戏,无非就是将j,j+1,……,k-1,k这些堆的sg值进行异或运算

但每轮游戏,会改变某一堆的sg值,我们不可能每轮游戏都遍历一遍[j,k]区间,然后得到异或值,这样复杂度太高,毕竟,如果区间[j,k]等于[1,n]的话,时间复杂度为O(n^2),显然不可取,所以我们利用线段树来解决这个问题,这样复杂度只有O(nlogn)

每次改变某堆sg值时,只需将线段树该堆代表的叶子节点进行更新,而查询第j堆到第k堆时,就是对区间[j,k]进行区间查询

具体实现看代码,不懂的欢迎提出

【时间复杂度&&优化】
O(mlogn)

题目链接→FZU Problem 2240 Daxia & Suneast's problem

 Source Code

/*Sherlock and Watson and Adler*/
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<math.h>
#include<vector>
#include<map>
#include<set>
#include<bitset>
#include<cmath>
#include<complex>
#include<string>
#include<algorithm>
#include<iostream>
#define eps 1e-9
#define LL long long
#define PI acos(-1.0)
#define bitnum(a) __builtin_popcount(a)
using namespace std;
const int N = 100005;
const int M = 100005;
const int inf = 1000000007;
const int mod = 1000000007;
struct tree
{
    int left,right,Xor;
}s[4*N];
int sg[N];
int solve(int x)
{
    while(x%2)
        x/=2;
    return x/2;
}
void push_up(int p)
{
    s[p].Xor=s[p*2].Xor^s[p*2+1].Xor;
}
void buildtree(int l,int r,int p)
{
    s[p].left=l,s[p].right=r;
    if(l==r)
    {
        s[p].Xor=sg[l];
        return ;
    }
    int mid=(l+r)/2;
    buildtree(l,mid,p*2);
    buildtree(mid+1,r,p*2+1);
    push_up(p);
}
void update(int l,int p,int x)
{
    if(s[p].left==s[p].right&&s[p].left==l)
    {
        s[p].Xor=x;
        return ;
    }
    int mid=(s[p].left+s[p].right)/2;
    if(l>mid)
        update(l,p*2+1,x);
    else
        update(l,p*2,x);
    push_up(p);
}
int query(int l,int r,int p)
{
    if(l==s[p].left&&r==s[p].right)
        return s[p].Xor;
    int mid=(s[p].left+s[p].right)/2;
    if(l>mid)
        return query(l,r,p*2+1);
    else if(r<=mid)
        return query(l,r,p*2);
    else
        return query(l,mid,p*2)^query(mid+1,r,p*2+1);
}
int main()
{
    int n,m,i,k,x,l,r;
    while(~scanf("%d%d",&n,&m))
    {
        for(i=1;i<=n;i++)
        {
            scanf("%d",&x);
            sg[i]=solve(x);
        }
        buildtree(1,n,1);
        for(i=0;i<m;i++)
        {
            scanf("%d%d%d%d",&k,&x,&l,&r);
            update(k,1,solve(x));
            if(query(l,r,1))
                puts("daxia");
            else
                puts("suneast");
        }
    }
    return 0;
}

菜鸟成长记

0
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

线段树详解(单点更新与成段更新\区间更新操作)

本文纯属原创,转载请注明出处,谢谢。 距离第一次接触线段树已经一年多了,再次参加ACM暑假集训,这一次轮到我们这些老家伙们给学弟学妹们讲解线段树了,所以就自己重新把自己做过的题目看了一遍,然后写篇博客...
  • u014705673
  • u014705673
  • 2015-07-06 15:57
  • 6234

树状数组的区间修改,单点查询

树状数组的区间修改
  • u013514722
  • u013514722
  • 2014-10-17 21:49
  • 2647

POJ - 2155 Matrix (二维树状数组 + 区间修改 + 单点求值 或者 二维线段树 + 区间更新 + 单点求值)

POJ - 2155 Matrix Time Limit: 3000MS   Memory Limit: 65536KB   64bit IO Format: %I64d & %I64u ...
  • qq_18661257
  • qq_18661257
  • 2015-08-07 20:16
  • 1223

hdu5475 An easy problem(线段树+单点更新+区间求积)

hdu5475题目给你n个操作,初始时有个1,每次操作给一个数u,操作1表示乘以u并输出结果,操作2表示除以第u的操作乘的数。不仅如此,要输出的是取余结果。思路想当年。。,想暴力,但取余是没有除法的,...
  • pibaixinghei
  • pibaixinghei
  • 2016-08-31 20:16
  • 199

线段树部分总结 (单点,区间)更新,区间求和,求最大值(敌兵布阵&I Hate It&A Simple Problem with Integers)

线段树 线段树是一种二叉搜索树 单点,区间更新,区间求和,区间求最大值 敌兵布阵 I Hate It A Simple Problem with Integers C国的死对头A国这段时间正在...
  • qq_37494296
  • qq_37494296
  • 2017-07-05 21:31
  • 296

FZU Problem 2136 取糖果(线段树离散化,区间合并)

Problem 2136 取糖果 Accept: 175    Submit: 499 Time Limit: 1000 mSec    Memory Limit : 32768 KB ...
  • yu_ch_sh
  • yu_ch_sh
  • 2015-12-07 16:23
  • 275

HDU 4267 A Simple Problem with Integers(2012年长春网络赛A 多颗线段树+单点查询)

以前似乎做过类似的不过当时完全不会。现在看到就有点思路了,开始还有洋洋得意得觉得自己有不小的进步了,结果思路错了。。。改了很久后测试数据过了还果断爆空间。。。 给你一串数字A,然后是两种操作: “...
  • qq_33530115
  • qq_33530115
  • 2016-09-01 20:16
  • 94

hdu 5475 An easy problem(线段树单点更新)

An easy problem Time Limit: 8000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)...
  • acm_cxq
  • acm_cxq
  • 2016-06-16 21:43
  • 244

POJ3468 A Simple Problem with Integers(线段树成段更新,区间查询)

A Simple Problem with Integers Time Limit: 5000MS   Memory Limit: 131072K Total Subm...
  • u013021513
  • u013021513
  • 2015-05-26 16:16
  • 339

POJ 3468 A Simple Problem with Integers 线段树(区间更新,查询,lazy数组)

A Simple Problem with Integers Time Limit: 5000MS Memory Limit: 131072K Total Sub...
  • u013014691
  • u013014691
  • 2015-03-01 19:32
  • 441
    博客通知
    访客统计
    Flag Counter
    个人资料
    • 访问:275040次
    • 积分:6104
    • 等级:
    • 排名:第4768名
    • 原创:318篇
    • 转载:13篇
    • 译文:0篇
    • 评论:393条
    博客专栏
    最新评论