#include<bits/stdc++.h>usingnamespace std;constint N =1e5+10;constint mod =1e9+7;//快读voidread(int&x){int res =0, f =1;char c =getchar();while(!isdigit(c)){if(c =='-') f =-1;
c =getchar();}while(isdigit(c)) res =(res <<1)+(res <<3)+(c -'0'), c =getchar();
x = res * f;}//快输voidOUT(int x){if(x <0) x =-x,putchar('-');if(x >9)OUT(x /10);putchar(x %10+'0');}voidprint(int a,char c){OUT(a),putchar(c);}//逆元intInv(int x){return(x ==1)? x :(x - x / mod)*Inv(x % mod)% mod;}//组合数structComb{/*后面还是要学会自己敲*/int re[N], inv[N], fac[N];voidinit(int n){
fac[0]= fac[1]=1;
inv[1]=1;//独独没有inv[0]
re[0]= re[1]=1;for(int i =2; i <= n; i++){
fac[i]= fac[i -1]* i % mod;
inv[i]=(mod - mod / i)* inv[mod % i]% mod;
re[i]= re[i -1]* inv[i]% mod;}}intC(int a,int b){return fac[a]* re[b]% mod * re[a - b]% mod;}};signedmain(){}/*
*/
题意:给定一个长度为
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],求有相交部分但一个线段不被另一个线段完全包含的线段对数(两个约束,当一个约束同时满足时,可二分等更复杂度极低的方法处理另一个约束)