2021“MINIEYE杯”中国大学生算法设计超级联赛(1)1006/HDU 6955 Xor sum

https://acm.hdu.edu.cn/showproblem.php?pid=6955

题意:

找最短的异或和>=k的连续子序列

先求前缀异或和a[]

问题转化成求l和r(l<r),满足a[r]^a[l]>=k且r-l最小

用字典树存储每个节点对应异或区间的最靠后的位置

把0位置异或0加进字典树

然后枚举右端点i

先查询能以i为右端点的最靠后的左端点

再把当前位置的前缀异或值加入字典树

查询方式:

从高到低枚举k的每一位,假设对应位当前前缀异或值是w(0或者1)

如果k的这一位是1,那么必须要走w^1方向。

如果k的这一位是0,那么先用w^1方向的最靠后位置更新,再走w方向。因为只要这一位的异或结果是1,就会满足>k的条件,只需要再找到这一位暂时相等,要通过后面的位决出大小的位置。

#include<bits/stdc++.h>

using namespace std;

#define N 100001

int tr[N*30][2],mx[N*30],tot;
int a[N]; 

int bit[30];

void add(int val,int pos)
{
    int w,now=0;
    for(int j=29;j>=0;--j)
    {
        w=(val>>j)&1;
        if(!tr[now][w]) tr[now][w]=++tot;
        now=tr[now][w];
        mx[now]=pos;
    }
}

int main()
{
    int T,n,k,al,ar,r,now,w;
    memset(mx,-1,sizeof(-1));
    scanf("%d",&T);
    while(T--)
    {
        al=1;
        ar=1e9;
        scanf("%d%d",&n,&k);    
        for(int i=0;i<30;++i) bit[i]=(k>>i)&1;
        for(int i=1;i<=n;++i) 
        {
            scanf("%d",&a[i]); 
            a[i]^=a[i-1];
        }
        add(0,0);
        for(int i=1;i<=n;++i)
        {            
            r=-1;
            now=0;
            for(int j=29;j>=0;--j)
            {
                w=(a[i]>>j)&1;
                if(!bit[j])
                {
                    r=max(r,mx[tr[now][w^1]]);
                    now=tr[now][w];
                }
                else now=tr[now][w^1];
                if(!now) break; 
            }
            r=max(r,mx[now]);
            if(r>-1 && i-r<ar-al)
            {
                al=r;
                ar=i;
            }
            add(a[i],i);
        }
        if(ar-al<n) printf("%d %d
",al+1,ar);
        else printf("-1
");
        for(int i=0;i<=tot;++i) 
        {
            mx[i]=-1;
            tr[i][0]=tr[i][1]=0;
        }
        tot=0;
    }
}
作者:xxy
本文版权归作者和博客园共有,转载请用链接,请勿原文转载,Thanks♪(・ω・)ノ。
原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/15071386.html