首先,区间不交的限制几乎可以不考虑,所以只要任意两个左右端点不重合,且异或和相等的区间即可。
其次,可以发现异或这个东西也没啥用。问题的症结在于值域太大,因此考虑将值域拆成两个部分,即二进制的前 k k k位和后 k k k位。
我们考虑找到若干对 ( i , j ) (i,j) (i,j),使得只考虑二进制前 k k k位时,其对应的异或前缀和相同。这样,我们至少能找到 2 k + 1 2^k+1 2k+1组这样的数对(也就对应一段区间),显然存在两个数对满足其对应的二进制后 k k k位的异或和相同。
现在回过头看,因为记录的是相同的前缀异或和的最大位置,所以端点之间一定不交,因此我们的构造是合法的。
这样,我们只从 2 2 k + 1 2^{2k+1} 22k+1个区间中取出 2 k + 1 2^{k+1} 2k+1个区间就完成了构造。这也是折半枚举在构造题中的应用:估计解的范围,从而减少枚举量。
类似的题目:小蓝的构造。
但是不知道为啥这道题直接暴搜就能过,难道有什么神奇的剪枝?
其实CF这道题乱搞一下也能过
#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
#define inf 0x3f3f3f3f
using namespace std;
int T,K,n,maxpos[1<<18];
pair<int,int>ps[1<<18];
ll a[(1<<18)+5],sm[(1<<18)+5];
void solve(){
cin>>K,n=1<<K+1;
for(int i=1;i<=n;i++)cin>>a[i],sm[i]=sm[i-1]^a[i];
for(int i=0;i<1<<K;i++)ps[i]={-1,-1},maxpos[i]=-1;
maxpos[0]=0;
for(int i=1;i<=n;i++){
if(~maxpos[sm[i]>>K]){
int j=maxpos[sm[i]>>K];
ll x=sm[i]^sm[j];
if(ps[x].fi!=-1){
int a=ps[x].fi,b=ps[x].se,c=j+1,d=i;
if(b<c)cout<<a<<" "<<b<<" "<<c<<" "<<d<<"\n";
else cout<<min(a,c)<<" "<<max(a,c)-1<<" "<<b+1<<" "<<d<<"\n";
return;
}
ps[x]={j+1,i};
}maxpos[sm[i]>>K]=i;
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>T;
while(T--){
solve();
}
}