题意:给定一个长度为
n
n
n的数组
a
a
a,求最大的区间满足条件:
a
[
l
]
&
a
[
l
+
1
]
&
.
.
.
&
a
[
r
−
1
]
&
a
[
r
]
>
a
[
l
]
⊕
a
[
l
+
1
]
⊕
.
.
.
⊕
a
[
r
−
1
]
⊕
a
[
r
]
a[l]\&a[l+1]\&...\&a[r-1]\&a[r]>a[l]\oplus a[l+1]\oplus ...\oplus a[r-1] \oplus a[r]
a[l]&a[l+1]&...&a[r−1]&a[r]>a[l]⊕a[l+1]⊕...⊕a[r−1]⊕a[r]。
每次遇到0要清空记录的位置,用一个set<int>记录需要清空的xor前缀的位置,多一个
log
n
\log n
logn。总复杂度为
O
(
n
log
n
)
O(n\log n)
O(nlogn),可以卡过。
官方题解:枚举每个 r 从1 到 n。对于每个 r ,找到满足以下三个条件的 l 。
要找到最小的 l 满足 r-l+1 为偶数。
[l,r] 在这一位全部为 1 。
[l,r] 在更高位的异或和为 0。
代码:
#include<bits/stdc++.h>// #define int long long#definedbg(x) cout << #x <<"==="<< x << endlusingnamespace std;template<classT>voidread(T &x){
T res =0, f =1;char c =getchar();while(!isdigit(c)){if(c =='-') f =-1;
c =getchar();}while(isdigit(c)) res =(res <<3)+(res <<1)+(c -'0'), c =getchar();
x = res * f;}constint N =(1<<20)+100;int n, a[N];int xo;int pre[N][2];
set<int> st;int ans =0;voidinit(){
xo =0;memset(pre,-1,sizeof(pre));
pre[xo][0%2]=0;
st.insert(xo);//细节,也是要被删除的}voidsolve(){read(n);for(int i =1; i <= n; i++)read(a[i]);for(int j =20; j >=0; j--){init();for(int i =1; i <= n; i++){
xo ^=(a[i]>>(j +1));//前缀和//该位为 1if((a[i]&(1<< j))>0){int p = pre[xo][i %2];//查找连续 1 && 异或值也为 xo 的位置if(p !=-1)
ans =max(ans, i - p);else
pre[xo][i %2]= i;
st.insert(xo);}else{for(auto k : st) pre[k][0]= pre[k][1]=-1;
st.clear();
pre[xo][i %2]= i;//细节
st.insert(xo);}}}
cout << ans;}signedmain(){solve();return0;}
题解2(只是写法不一样):
i 从 1 到 n ,记录 1 到 i-1 的异或前缀的位置,到 i 的时候就找前一位 p=pre[xo[i]] ,如果 p+1 到 i 这一位全为 1 ,则更新ans=max(ans,i-p),同时 pre[xo[i]] 不需要更新;如果不全为 1 ,说明 pre[xo[i]] 已经 “out” 了,需要更新。(u1s1,雀食有点绕)
大概就这样?,复杂度为
O
(
n
log
n
)
O(n\log n)
O(nlogn)。
代码:
#include<bits/stdc++.h>// #define int long long#definedbg(x) cout << #x <<"==="<< x << endlusingnamespace std;template<classT>voidread(T &x){
T res =0, f =1;char c =getchar();while(!isdigit(c)){if(c =='-') f =-1;
c =getchar();}while(isdigit(c)) res =(res <<3)+(res <<1)+(c -'0'), c =getchar();
x = res * f;}constint N =(1<<20)+100;int n, a[N];int sum[N], xo[N];int pre[N][2], ans =0;voidsolve(){read(n);for(int i =1; i <= n; i++)read(a[i]);for(int j =20; j >=0; j--){
sum[0]= xo[0]=0;for(int i =1; i <= n; i++){
sum[i]= sum[i -1]+((a[i]&(1<< j))>0);
xo[i]= xo[i -1]^(a[i]>>(j +1));}memset(pre,-1,sizeof(pre)), pre[0][0]=0;for(int i =1; i <= n; i++){if(pre[xo[i]][i %2]==-1)
pre[xo[i]][i %2]= i;else{int p = pre[xo[i]][i %2];if(sum[i]- sum[p]== i - p)
ans =max(ans, i - p);else
pre[xo[i]][i %2]= i;}//奇偶不同位也要检查,如果不是连续的1,要令它变成-1。//————每次更新pre[xo[i]][0|1](都要更新,只需要关注:下一次操作的应该是满足条件的!!!)int p = pre[xo[i]][~(i %2)];if(sum[i]- sum[p]!= i - p) pre[xo[i]][~(i %2)]=-1;}}
cout << ans;}signedmain(){solve();return0;}
题目3(cf2100,与题目4要求完全相反,题目4cf2200更难):给定
n
≤
5
e
5
n\le 5e5
n≤5e5条线段
[
x
,
y
]
[x,y]
[x,y],求有相交部分但一个线段不被另一个线段完全包含的线段对数(两个约束,当一个约束同时满足时,可二分等更复杂度极低的方法处理另一个约束)