題目大意
給出一個長爲\(2^{K+1}\)的序列,每個元素在\([0,4^K)\)之間,
在序列中找到兩個不相交的區間使得二者的異或和相等
\(K<=17,\sum 2^{K+1}<=2^{18}\)
題解
好題,但最後沒有break出去絕殺失敗了,rk70=>rk150
因爲元素大小是\(4^K\)級別的,和大小相關的算法(FWT)都沒用了,所以不如直接隨機
發現長度爲\(2^{K+1}\)的序列裏有\(2^K(2^{K+1}+1)\)個區間,即大於\(2*4^K\)個,那麼這些區間的取值分配到\([0,4^K)\)平均每個位置有大於2個
考慮隨機,發現如果按照異或值s隨機,可以構造把區間集中到某些特定的s上,那樣隨機次數會變多(如果一個s只有<=1個區間顯然選不出來),同時需要找兩個區間
於是考慮直接隨機一個區間[x,y],然後找另一個符合異或值s的區間:
直接把r從1=>y-1,用map找l;然後把l從n=>x+1,用map找r(\(n=2^{K+1}\))
這樣找到的區間可能有交,但只要不完全包含就可以去掉交集使其合法
期望隨機次數\(O(1)\),一次\(O(n\log)\),實際跑挺快
code
#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define ll long long
#define file
using namespace std;
int T,K,n,i,j,k,l,x,y,X1,Y1,X2,Y2;
ll a[262254],sum;
bool Find;
map<ll,int> mp;
map<ll,int> :: iterator I;
int random(int x,int y)
{
return (32768ll*rand()+rand())%(y-x+1)+x;
}
int main()
{
srand(time(0));
// freopen("C.in","r",stdin);
scanf("%d",&T);
for (;T;--T)
{
scanf("%d",&K);n=1<<(K+1);
fo(i,1,n) scanf("%lld",&a[i]),a[i]^=a[i-1];
Find=0;
fo(l,1,100)
{
x=random(1,n);
y=random(1,n);
if (x>y) swap(x,y);
sum=a[y]^a[x-1];
mp.clear();
fo(i,1,n)
{
if (i<x)
mp[a[i-1]]=i;
I=mp.find(sum^a[i]);
if (I!=mp.end())
{
X1=I->second;
Y1=i;
X2=x;
Y2=y;
if (X2<=Y1)
{
swap(X2,Y1);
--Y1;
++X2;
}
if (X1<=Y1 && X2<=Y2)
{
Find=1;
break;
}
}
}
if (Find==1) break; //就是這裏沒break
if (x==2 && y==2)
n=n;
mp.clear();
fd(i,n,x+1)
{
if (y<i)
mp[a[i]]=i;
I=mp.find(sum^a[i-1]);
if (I!=mp.end())
{
X1=i;
Y1=I->second;
X2=x;
Y2=y;
swap(X1,X2);
swap(Y1,Y2);
if (X2<=Y1)
{
swap(X2,Y1);
--Y1;
++X2;
}
if (X1<=Y1 && X2<=Y2)
{
Find=1;
break;
}
}
}
if (Find==1) break; //就是這裏沒break
}
if (!Find)
printf("%d\n",-1);
else
printf("%d %d %d %d\n",X1,Y1,X2,Y2);
}
}