牛客练习赛53-E 老瞎眼 pk 小鲜肉
老瞎眼 pk 小鲜肉
https://ac.nowcoder.com/acm/problem/50444
这题的题意大概是
给出一段长度为 的区间
次询问求 ~ 这个区间内 最短的一段区间 ~
使得
诶 离线么?树状数组好像不好做啊 因为大多数人只会单点修改区间修改和差分吧
考虑离线+线段树
我们先记录一个
那么我们用一个类似桶一样的东西 记录上一个出现 的位置
显然这题是个单点修改 求区间最小值
考虑移动 右指针
把询问的右端点为 的存在一起 这样就省下来一个排序
我们要记录 点对区间的贡献
应该反过来做 考虑 最后一次出现的位置
然后对 进行单点修改 能保证这个肯定对于这个点来说向右偏移最小的值使得异或和为0
因为移动的是右端点 右端点往右的区间和当前区间是互为独立的 或者换句话说 右端点往右的区间对当前区间是没有贡献的即对答案不会影响
然后直接大力查询 就可以了
#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 ; }