Game(HDU-6610)

Problem Description

Again Alice and Bob is playing a game with stones. There are N piles of stones labelled from 1 to N, the i th pile has ai stones. 

First Alice will choose piles of stones with consecutive labels, whose leftmost is labelled with L and the rightmost one is R. After, Bob will choose another consecutive piles labelled from l to r (L≤l≤r≤R). Then they're going to play game within these piles.

Here's the rules of the game: Alice takes first and the two will take turn to make a move: choose one pile with nonegetive stones and take at least one stone and at most all away. One who cant make a move will lose.

Bob thinks this game is not so intersting because Alice always take first. So they add a new rule, which is that Bob can swap the number of two adjacent piles' stones whenever he want before a new round. That is to say, if the i th and i+1 pile have ai and ai+1 stones respectively, after this swapping there will be ai+1 and ai.

Before today's game with Bob, Alice wants to know, if both they play game optimally when she choose the piles from L to R, there are how many pairs (l, r) chosed by Bob that will make Alice *win*.

Input

Input contains several test cases.

For each case:

The fisrt line contains N, M. N is mentioned aboved ans M is the number of the sum of game rounds and the times of Bob's swapping.

The second line contains N integars a1,a2,...an, indicating the number of each piles' stones.

The next M lines will have an integar opt (1≤opt≤2), indicating the type of operation.

If opt equals 1, then L and R follow. Alice and Bob start a new round and Alice choose L and R as mentioned. 

If opt equals 2, then POS follows. Bob will swap the piles labelled POS and POS+1.

0≤ai≤1,000,000
1≤N,M≤100,000,∑N,∑M<600,000
1≤L≤R≤N
1≤POS<N

Output

For each case:

For each opt which equals 1, you shall output one line with an integar indicating the number of pairs (l,r) that will make Alice win the round.

Sample Input

3 3
4 0 4
2 2
2 1
1 1 3

Sample Output

3

 

题意:多组样例,每组给出 n 个数,每个数代表一堆石头的个数,再给出 m 次操作,操作分为 1 l r,代表在 [l,r] 区间内做 NIM 博弈,问先手必胜的种类数,2 x 代表将数 a[x] 与 a[x+1] 交换

思路:

首先,NIM 博弈的必胜条件是:所有数字的异或和大于 0 就是先手必胜,否则先手必败

根据异或的性质,预处理出前缀异或和,然后就可以在 O(1) 内判断胜负

因此每个区间 [l,r] 的胜负,只要看前缀和是否满足 sum[l-1]^sum[r]=0,也即看 sum[l-1] 与 sum[r] 是否相同即可,于是问题就变为了求区间 [l-1,r] 中相同数字的对数

由于题目没有要求强制在线,因此使用带修莫队即可解决

因此,然后对于给出的 n 个数,首先求出这 n 个数的异或前缀和,然后记录下 m 次要做的操作,再利用带修莫队来进行修改

每次统计新的时,ans 加上已有的 cnt[x],cnt[x] 再加上新的一个;每次删除旧的时,cnt[x] 首先减去旧的一个,ans 再减去已有的 cnt[x];而每次时间轴发生改变,利用异或前缀和,同样是进行添加与删除

Source Program

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<unordered_map>
#include<bitset>
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LL long long
#define Pair pair<LL,LL>
LL quickPow(LL a,LL b){ LL res=1; while(b){if(b&1)res*=a; a*=a; b>>=1;} return res; }
LL quickModPow(LL a,LL b,LL mod){ LL res=1; a=a%mod; while(b){if(b&1)res=(a*res)%mod; a=(a*a)%mod; b>>=1;} return res; }
LL getInv(LL a,LL mod){ return quickModPow(a,mod-2,mod); }
LL GCD(LL x,LL y){ return !y?x:GCD(y,x%y); }
LL LCM(LL x,LL y){ return x/GCD(x,y)*y; }
const double EPS = 1E-10;
const int MOD = 998244353;
const int N = 1000000+5;
const int dx[] = {-1,1,0,0,1,-1,1,1};
const int dy[] = {0,0,-1,1,-1,1,-1,1};
using namespace std;

struct Node{
    int l,r;//询问的左右端点
    int time;//时间维度
    int id;//询问的编号
}q[N];

int n,m,a[N];
int sum[N];//异或前缀和
int pos[N];//要改的位置
int numQ,numC;//查询、修改操作的次数
int block;//分块
LL ans,cnt[N];
LL res[N];
 
bool cmp(Node a,Node b){//排序
    return (a.l/block)^(b.l/block) ? a.l<b.l : ((a.r/block)^(b.r/block)?a.r<b.r:a.time<b.time);
}
 
void add(int x){//统计新的
    ans+=cnt[x];
    cnt[x]++;
}
void del(int x){//减去旧的
    cnt[x]--;
    ans-=cnt[x];
}
void change(int l,int r,int ti){//改变时间轴
    int x=pos[ti];
    if(x>=l&&x<=r){//删除指定位置上的值
        cnt[sum[x]]--;
        ans-=cnt[sum[x]];
    }
    sum[x]^=a[x];
    sum[x]^=a[x+1];
    if(x>=l&&x<=r){//统计新的数
        ans+=cnt[sum[x]];
        cnt[sum[x]]++;
    }
    swap(a[x],a[x+1]);//直接互换,下次执行时必定是回退这次操作
}
int main(){
    while(scanf("%d%d",&n,&m)!=EOF){
        ans=0;
        numQ=0;
        numC=0;
        memset(cnt,0,sizeof(cnt));
        block=pow(n,0.66666);//分块
 
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            sum[i]=sum[i-1]^a[i];
        }
 
        for(int i=1;i<=m;i++){
            int op;
            scanf("%d",&op);
            if(op==1){//查询操作
                int x,y;
                scanf("%d%d",&x,&y);
                numQ++;//查询操作次数+1
                q[numQ].l=x-1;
                q[numQ].r=y;
                q[numQ].id=numQ;//序号
                q[numQ].time=numC;//时间轴
            }
            else{//修改操作
                int x;
                scanf("%d",&x);
                numC++;//修改操作次数+1
                pos[numC]=x;
            }
        }

        sort(q+1,q+numQ+1,cmp);//对询问进行排序
        int l=1,r=0,time=0;//左右指针与时间轴
        for(int i=1;i<=numQ;i++){
            int ql=q[i].l,qr=q[i].r;//询问的左右端点
            int qtime=q[i].time;//询问的时间轴
            while(l>ql) add(sum[--l]);//[l-1,r,time]
            while(l<ql) del(sum[l++]);//[l+1,r,time]
            while(r<qr) add(sum[++r]);//[l,r+1,time]
            while(r>qr) del(sum[r--]);//[l,r-1,time]
            
            while(time<qtime) change(l,r,++time);//[l,r,time+1]
            while(time>qtime) change(l,r,time--);//[l,r,time-1]
            
            LL len=q[i].r-q[i].l;
            res[q[i].id]=len*(len-1)/2+len-ans;//获取答案
        }
 
        for(int i=1;i<=numQ;i++)
            printf("%lld\n",res[i]);
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值