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();
}

浙公网安备 33010602011771号