Loading

2021hdu多校第一场1006-XOR SUM

题意:给定一个\(n\)个数,求最短的连续段使其异或和不小于给定的数\(k\)。若有多处答案,优先左端点最靠前的。

题解:

  • 一个常见的区间异或和的套路是首先转成前缀异或和,则原问题转化成在\(n\)​个数中找最近的两个数使得其异或和不小于\(k\)​.,这样答案长度是求得的最小值减一
  • \(d \;xor\; p \geq k\)​​​​​​​ 只能按位考虑,所以考虑建\(trie\)​​​​​​​​树来解决,从左向右扔数进去,用一个数组维护一下树上的节点最后一次被访问到时数的下标,更新时把经过的节点的更新一下,统计答案时就相当于枚举右端点,根据异或\(\geq k\)​​​​​​的要求遍历节点并且更新答案。复杂度是\(O(nlogn)\)​的。
  • \(trie\)树数组一般开大点\((3e6)\)

代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
const ll inf=1e16;
int n,m;
struct trie{
    int vis[2];
}all[(int)(3e6+10)];
int tot;
int yuan[maxn],qian[(int)(3e6+10)];
int ans,lo1,ro1;
void sol(){
    cin>>n>>m;
    tot=1;ans=inf;
    all[1].vis[0]=all[1].vis[1]=0;
    for(int i=1;i<=n;i++) cin>>yuan[i],yuan[i]^=yuan[i-1];
    for(int i=1;i<=n;i++){
        int now0=yuan[i],now2=1,ans1=-1;
        for(int j=30;j>=0;j--){
            int v=(m&(1LL<<j))?1:0;
            int v1=(now0&(1LL<<j))?1:0;
            if(v) now2=all[now2].vis[!v1];
            else{
                if(all[now2].vis[!v1]) ans1=max(ans1,qian[all[now2].vis[!v1]]);
                now2=all[now2].vis[v1];
            }
            if(!now2) break;//
        }
        if(now2) ans1=max(ans1,qian[now2]);////
        if(ans1!=-1&&i-ans1+1<ans) ans=min(ans,i-ans1+1),lo1=ans1,ro1=i;
        now2=1;
        for(int j=30;j>=0;j--){
            int v=(now0&(1LL<<j))?1:0;
            if(!all[now2].vis[v]){all[now2].vis[v]=++tot;all[all[now2].vis[v]].vis[0]=all[all[now2].vis[v]].vis[1]=0;}
            now2=all[now2].vis[v];
            qian[now2]=i;
        }
    }
    if(ans!=inf) cout<<lo1+1<<' '<<ro1<<endl;
    else cout<<-1<<endl;
}
signed main(){
    int t;cin>>t;
    for(int i=1;i<=t;i++) sol();
}
posted @ 2021-07-21 23:41  14long  阅读(19)  评论(0)    收藏  举报