题目大意
给出一个长为\(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);
}
}
标签:map,int,区间,Twin,CF1835C,随机,X2,Clusters,Y2
From: https://www.cnblogs.com/gmh77/p/17490274.html