第十五届吉林程序设计省赛The 15th Jilin Provincial Collegiate Programming Contest I. Nim Game(树状数组,线性基)

文章目录

题目链接


题意

N i m Nim Nim 游戏,轮流从几堆石头中取出石头。在每一轮,玩家必须从一堆石头中取出一颗或多颗石头。不能拿石头的人输掉比赛。
戴安娜和爱娃可以每轮可以进行两个操作
o p = = 1 op==1 op==1, 在一个区间内选出几堆进行 N i m Nim Nim 游戏,艾娃先手,戴安娜选牌堆,问戴安娜是否有机会胜利;
o p = = 2 op==2 op==2, 给 [ l , r ] [l,r] [l,r] 的石头堆 a [ i ] a[i] a[i] 加上 x x x 个石头。

题解

N i m Nim Nim 博弈的结论是 所有 a [ i ] a[i] a[i]异或起来等于 0 0 0,先手必败。

所以 o p 1 op1 op1 就是询问 a [ l . . . r ] a[l...r] a[l...r] 内能否选出若干数使得异或为 0 0 0
然后我们知道 线性基里面的任意一些数异或起来都不能得到 0 ,
那么问题最后转化为询问 [ l , r ] [l,r] [l,r] 的序列是否为线性基,是线性基那么先手必胜,戴安娜必败,假如不是线性基,那么就能找出任意一些数异或起来等于 0 0 0使得戴安娜胜。

o p 2 op2 op2 是区间操作可以用线段树或者树状数组差分

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e6+5;
int n;
int a[N];
int c[N],b[N];
int lowbit(int x) {
    return x&(-x);
}
void add(int i, int k) {
    while(i<=n) {
        c[i]+=k;
        i+=lowbit(i);
    }
}
int getsum(int i) {
    int res=0;
    while(i>0) {
        res+=c[i];
        i-=lowbit(i);
    }
    return res;
}
bool insert(int x) {
    for(int i=30; i>=0; i--) {
        if(x&(1<<i)) {
            if(b[i]) x^=b[i];
            else {
                b[i]=x;
                return 0;
            }
        }
    }
    return 1;
}
signed main() {
   ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
   int q;
   cin>>n>>q;
   for(int i=1; i<=n; i++) {
        cin>>a[i];
   }
   while(q--) {
        int op,l,r;
        cin>>op>>l>>r;
        if(op==1) {
            int x;
            cin>>x;
            add(l, x);
            add(r+1, -x);
        }else {
            bool fl=0;
            for(int i=0; i<=30; i++) b[i]=0;
            for(int i=l; i<=r; i++) {
                if(insert(a[i]+getsum(i))) {
                        fl=1;
                        break;//因为1e9范围最长的线性基为31,所以不会t
                }
            }
            if(fl) cout<<"Yes"<<endl;
            else cout<<"No"<<endl;
        }
   }
   return 0;
}

总结

Qaq

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值