牛客练习赛53-E 老瞎眼 pk 小鲜肉
这题的题意大概是
给出一段长度为\(n\) 的区间
\(q\)次询问求 \(L\)~ \(R\) 这个区间内 最短的一段区间 \(l\)~\(r\)
使得 \(\oplus_{i=l}^{r} a_j= 0\)
\(L<=l<r<=r\)
诶 离线么?树状数组好像不好做啊 因为大多数人只会单点修改区间修改和差分吧
考虑离线+线段树
我们先记录一个 \(sum_i = \oplus_{j=1}^i a_j\)
那么我们用一个类似桶一样的东西 \(pos_i\) 记录上一个出现\(sum_i\) 的位置
显然这题是个单点修改 求区间最小值
考虑移动 右指针 \(r\)
把询问的右端点为\(r\) 的存在一起 这样就省下来一个排序
我们要记录 \(l\) 点对区间的贡献
应该反过来做 考虑 \(sum_j\) 最后一次出现的位置 \(pos_{sum_j}\)
然后对 \(pos_{sum_j}\) 进行单点修改 能保证这个肯定对于\(pos_{sum_j}\)这个点来说向右偏移最小的值使得异或和为0
因为移动的是右端点 右端点往右的区间和当前区间是互为独立的 或者换句话说 右端点往右的区间对当前区间是没有贡献的即对答案不会影响
然后直接大力查询 \(query(l,r)\) 就可以了
#include<bits/stdc++.h>
using namespace std ;
int n , q ;
const int N = 5e5 + 5 ;
int sum[N] ;
vector < pair < int , int > > v[N] ;
int mn[N << 2] ;
int pos[N << 2] ;
int used[N] ;
int ans[N << 1] ;
inline void build(int l , int r , int rt) {
mn[rt] = INT_MAX ;
if(l == r) return ;
int mid = l + r >> 1 ;
build(l , mid , rt << 1) ;
build(mid + 1 , r , rt << 1 | 1) ;
}
inline void change(int x , int l , int r , int rt , int val) {
if(l == r) { mn[rt] = val ; return ; }
int mid = l + r >> 1 ;
if(x <= mid) change(x , l , mid , rt << 1 , val) ;
else change(x , mid + 1 , r , rt << 1 | 1 , val) ;
mn[rt] = min(mn[rt << 1] , mn[rt << 1 | 1]) ;
}
inline int query(int a , int b , int l , int r , int rt) {
if(a <= l && r <= b) return mn[rt] ;
int mid = l + r >> 1 ;
int ans = INT_MAX ;
if(a <= mid) ans = min(ans , query(a , b , l , mid , rt << 1)) ;
if(b > mid) ans = min(ans , query(a , b , mid + 1 , r , rt << 1 | 1 )) ;
return ans ;
}
#define fi first
#define se second
signed main() {
scanf("%d %d" , & n , & q) ;
for(register int i = 1 ; i <= n ; i ++) {
int x ; scanf("%d" , & x) ;
sum[i] = sum[i - 1] ^ x ;
}
for(register int i = 1 ; i <= q ; i ++) {
int l , r , id ; scanf("%d %d" , & l , & r) ; id = i ;
v[r].push_back({l , id}) ;
}
build(1 , n , 1) ;
for(register int i = 1 ; i <= n ; i ++) {
pos[sum[i - 1]] = i ;
int p = pos[sum[i]] ;
if(p && ! used[p]) {
change(p , 1 , n , 1 , i - p + 1) ;
used[p] = 1 ;
}
for ( auto x : v[i] ) ans[x.se] = query(x.fi , i , 1 , n , 1) ;
}
for(register int i = 1 ; i <= q ; i ++) printf("%d\n" , ans[i] == INT_MAX ? -1 : ans[i]) ;
return 0 ;
}