codeforce gym 100307H Hack Protection

原题地址:http://codeforces.com/gym/100307/problem/H

 

题意:

给定一个序列,求序列的子区间中,满足子区间XOR值等于AND值得子区间个数。

 

题解:

一直以为NEERC这种有名的比赛应该题解到处都是,太天真了……

首先考虑区间的AND值。

对于固定起点的区间,因为 & 的性质,AND值必然单调递减,并且,根据AND值中1的个数划分后面的区间,必然最多只能划分成32段,对于某一段中的任意位置,它和固定的起点所形成的子区间的AND值是一样的。

再来考虑区间的XOR值

对于数列A[],定义sum(a,b)=A[a]^A[a+1]^...^A[b];

那么sum(a,b)=(A[1]^A[2]^...^A[a-1])^((A[1]^A[2]^...^A[a-1])^(A[a]^A[a+1]^...^A[b])=sum(1,a-1)^sum(1,b),这是一个前缀和形式

考虑题给的等式,AND(a,b)==XOR(a,b)

也就是AND(a,b)==XOR(1,a-1)^XOR(1,b)

也就是AND(a,b)^XOR(1,a-1)==XOR(1,b) ——(1)

我们枚举区间的起点,XOR(1,a-1)是定值,接下来枚举AND值相同的区段,最多32段,在每一段中寻找满足等式(1)的b,累计答案

区间AND值可以用RMQ思想处理,区段的划分可以二分处理,而寻找区段中XOR值为固定值的元素个数,可以转化为前缀和

 

#include<bits/stdc++.h>

#define clr(x,y) memset((x),(y),sizeof(x))

using namespace std;
typedef long long LL;

struct seg
{
    int l;
    int val;
};

const int maxn=1e5;

int AND[maxn+5][18];
int XOR[maxn+5];
int n;
int A[maxn+5];
vector <seg> v[maxn+5]; //段划分
map <int,vector<int> > mp; //mp[x]中储存XOR[1,a]==x的元素下标a

void build() //计算区间AND值
{
    for (int i=1;i<=n;++i)
        AND[i][0]=A[i];

    for (int j=1;(1<<j)<=n;++j)
    {
        for (int i=0;i+(1<<j)-1<=n;++i)
        {
            AND[i][j]=AND[i][j-1] & AND[i+(1<<(j-1))][j-1];
        }
    }
}

int calu(int l,int r)
{
    int k=0;
    while ((1<<(k+1))<=r-l+1) ++k;
    return AND[l][k] & AND[r-(1<<k)+1][k];
}

LL Count(int l,int r,int val) //区间[l,r]中XOR值为val的元素个数
{
    return upper_bound(mp[val].begin(),mp[val].end(),r)-lower_bound(mp[val].begin(),mp[val].end(),l);
}

void solve()
{
    ++n;
    A[n]=0;//为了便于划分在末尾加上一个0

    build();
    for (int i=1;i<=n;++i) v[i].clear();

    for (int i=1;i<=n;++i)
    {
        int val=A[i];
        int l=i;
        int r=n;

        v[i].push_back((seg){l,val});
        while (val!=0) //不断二分划分段
        {
            int lb=l;
            int ub=r;
            while (ub-lb>1)
            {
                int mid=(ub+lb)>>1;
                if (calu(i,mid)==val)
                {
                    lb=mid;
                }
                else
                {
                    ub=mid;
                }
            }
            l=ub;
            val=calu(i,l);

            v[i].push_back((seg){l,val});
        }
    }

    mp.clear();
    XOR[0]=0;
    for (int i=1;i<=n;++i)
    {
        XOR[i]=XOR[i-1]^A[i];
        mp[XOR[i]].push_back(i);
    }

    LL ans=0;
    for (int i=1;i<=n;++i)
    {
        for (int j=0;j<v[i].size();++j)
        {
            int l=v[i][j].l;
            int r=(j==v[i].size()-1)? n:v[i][j+1].l-1;
            int val=v[i][j].val^XOR[i-1];

            ans+=Count(l,r,val);
            //printf("%d %d %d\n",l,r,val);
        }
    }

    //消除最后那个虚拟的0对答案的影响
    LL extra=1;
    int xor_tmp=0;
    for (int i=n-1;i>=1;--i)
    {
        xor_tmp^=A[i];
        if (xor_tmp==0) ++extra;
    }
    printf("%lld\n",ans-extra);
}

int main(void)
{
    #ifdef ex
    freopen ("../in.txt","r",stdin);
    //freopen ("../out.txt","w",stdout);
    #endif

    freopen("hack.in","r",stdin);
    freopen("hack.out","w",stdout);

    while (scanf("%d",&n)==1)
    {
        for (int i=1;i<=n;++i)
        {
            scanf("%d",&A[i]);
        }
        solve();
    }
}

 

posted on 2016-09-04 14:54  zhong_wang  阅读(280)  评论(0编辑  收藏  举报

导航