51nod3438 数组游戏

本文探讨了一种名为3438数组游戏的策略,玩家甲需要通过询问和操作来确保在给定初始状态下获得必胜优势。通过分析规则和计算方法,解决先手是否必胜的问题,适用于不同规模的数组并考虑了询问数量和白色格子分布。
摘要由CSDN通过智能技术生成

3438 数组游戏

有一个长度为N的数组,甲乙两人在上面进行这样一个游戏:首先,数组上有一些格子是白的,有一些是黑的。然后两人轮流进行操作。每次操作选择一个白色的格子,假设它的下标为x。接着,选择一个大小在1~n/x之间的整数k,然后将下标为x、2x、…、kx的格子都进行颜色翻转。不能操作的人输。现在甲(先手)有一些询问。每次他会给你一个数组的初始状态,你要求出对于这种初始状态他是否有必胜策略。

输入

第一行一个整数N ,表示数组的长度。
第二行一个整数 K ,表示询问的个数。
接下来 2K 行,每两行表示一次询问。
在这两行中,第一行一个正整数 W,表示数组中有多少个格子是白色的,第二行则有W 个 
1~N 之间的正整数,表示白色格子的对应下标。

输出

对于每个询问,若先手必胜输出Yes,否则输出No。答案之间用换行隔开。

数据范围

对于30%的数据,N<=20;
对于50%的数据,N<=1000000;
对于70%的数据,N<=10000000;
对于 100% 的数据,N<= 1000000000,K,W<=100 ,不会有格子在同一次询问中多次出现。

输入样例

3
2
2
1 2
2
2 3

输出样例

Yes
No

这里不放解析,直接放代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<climits>
#include<ctime>
using namespace std;
template<typename T>bool chkmax(T &_,T __){return _<__ ? (_=__,1) : 0;}
template<typename T>bool chkmin(T &_,T __){return _>__ ? (_=__,1) : 0;}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define inf INT_MAX
const int maxn=1e9+10;
const int maxm=100+10;
int n,q,m,a[maxm],SG[maxm*maxm],L[maxn/10000],R[maxn/10000],cnt;
int va1[maxn/10000],va2[maxn/10000],block;
bool in[maxn/10000];
int get(int x){return x<=block ? va1[x] : va2[n/x];}
void into(int x,int va){
    if(x<=block)va1[x]=va;
    else va2[n/x]=va;
}
void init(){
    for(int l=1,r;l<=n;l=r+1){
        r=n/(n/l);
        ++cnt;
        L[cnt]=l;
        R[cnt]=r;
    }
    DREP(i,cnt,1){
        mem(in);
        int b=n/L[i],sum=0;
        for(int l=1,r;l<=b;l=r+1){
            int c=b/l,tmp=get(L[i]*l);
            r=b/c;
            in[sum]=1;
            in[sum^tmp]=1;
            if((r-l+1)%2)sum^=tmp;
        }
        REP(j,0,maxn)if(!in[j]){
            into(L[i],j);
            break;
        }
    }
}
int main(){
    File();
    scanf("%d%d",&n,&q);
    block=sqrt(n);
    init();
    REP(i,1,q){
        int ans=0;
        scanf("%d",&m);
        REP(j,1,m){
            int u;
            scanf("%d",&u);
            ans^=get(u);
        }
        if(ans)puts("Yes");
        else puts("No");
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值