HDU6610——Game——(待修改莫队+Nim游戏)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6610

题目大意:

Alice和Bob在玩石子游戏,Alice选择一个区间【L,R】,确定玩游戏的区间,Bob再选择一个【l,r】,L<=l<=r<=R 

因为每次Alice可以先走,所以为了让游戏更加公平,Bob可以在每次游戏开始的时候先选择一个点pos,将pos与pos+1的石头堆

的石头数量交换。现在给你很多种操作和询问,

op==1,就会给你一个[L,R]的区间,你要回答在这个区间内有多少个l,r,Bob选择了之后Alice可以赢

op==2,表示Bob将pos和pos+1的石头进行了对换。

思路:

考虑没有任何骚操作的情况,Alice在选择了一个区间后,有多少个子区间l,r,Alice是必败的?

很显然这是一个简单的NIm游戏,只要这个子区间的异或和为0,那么Bob选择这个子区间后Alice就必败

 那么什么情况下区间的异或和为0呢?只要区间起点和终点的前缀异或和相同,那么这个区间异或和就为0

所以问题就转化为了:给你一个区间,询问这个区间中相同的数的个数有多少对?

是不是莫队模板题?没错,不过现在多了个修改操作,那就带修改莫队模板嘛,冲就完了。

离散化超时了,开了个1e6数组

#include <bits/stdc++.h>
using namespace std;

/*
    单点修改莫队板子
*/
typedef long long ll;



const int maxn=1e5+10;
const int maxm=1e6+10;
//const int maxm=1e6+10;
int a[maxn];
//询问
ll sum[maxn],p[maxn],block;//分块,带修改的分块最好为2/3
struct Query{
    int l,r;
    int id;
    int t;//时间轴,因为有修改操作
    Query(int _l,int _r,int _id,int _t):l(_l),r(_r),id(_id),t(_t){}
    Query(){}
}q[maxn];
int cmp(Query x,Query y){
    if(x.l/block!=y.l/block) return x.l/block<y.l/block;
    if(x.r/block!=y.r/block) return x.r/block<y.r/block;
    return x.t<y.t;
}
ll ANS[maxn];//答案
ll ans;
ll bar[maxm];//区间不同的数
/*
离散化超时了。。。
vector<ll>v;//离散化
int getid(int cur){
    return lower_bound(v.begin(),v.end(),cur)-v.begin()+1;
}
*/

//莫队加减模板
void add(int pos){
    ans+=bar[sum[pos]];
    bar[sum[pos]]++;
}
void del(int pos){
    ans-=--bar[sum[pos]];
}
//待修改的莫队
void modify_t(int i,int t){
    int flag=q[i].l<=p[t]&&p[t]<=q[i].r;//在这个询问区间内
    if(flag){
        del(p[t]);
    }
    sum[p[t]]^=a[p[t]],swap(a[p[t]],a[p[t]+1]),sum[p[t]]^=a[p[t]];//相当于先减去,再交换,再加上来
    if(flag){
        add(p[t]);
    }
}
signed main(){
   int n,m;
   while(scanf("%d%d",&n,&m)!=EOF){
        //v.clear();
        for(int i=0;i<maxn;i++)bar[i]=0;
        block=max(1ll*10,(ll)pow(n,2.0/3));
        sum[0]=0;
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            sum[i]=sum[i-1]^a[i];
            //v.push_back(sum[i]);
        }
        int qcnt=0,kcnt=0;
        //sort(v.begin(),v.end());
      // v.erase(unique(v.begin(),v.end()),v.end());
        while(m--){
            int op;
            scanf("%d",&op);
            if(op==1){
                int l,r;
                scanf("%d%d",&l,&r);
                q[++qcnt]=Query(l-1,r,qcnt,kcnt);//kcnt用来表示时间,以修改操作改变时间
            }
            else{
                int t;
                scanf("%d",&t);
                p[++kcnt]=t;
            }
        }
        sort(q+1,q+qcnt+1,cmp);
        int dexl=1,dexr=0,tt=0;ans=0;
        for(int i=1;i<=qcnt;++i){
            while(dexr<q[i].r) add(++dexr);
            while(dexl>q[i].l) add(--dexl);
            while(dexr>q[i].r) del(dexr--);
            while(dexl<q[i].l) del(dexl++);
            while(tt<q[i].t) modify_t(i,++tt);
            while(tt>q[i].t) modify_t(i,tt--);
            ANS[q[i].id]=(ll)(q[i].r-q[i].l)*(q[i].r-q[i].l+1)/2-ans;
        }
        for(int i=1;i<=qcnt;i++){
            printf("%lld\n",ANS[i]);
        }
   }
   return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值