题目大意
给一个长度为N的序列,有M个询问,每次询问区间内的数是否全都不一样,如果全都不一样就输出OK,如果有重复的就输出从右到左第一个重复的数。
解题思路
主席树在线非常直观,每一个数对应一个历史版本,然后询问的时候询问那个区间对应的线段树里面有多少个元素,如果最大值是1就表示OK,否则的话就输出最右的非1,对于位置做做处理就行。
线段树离线就不太好办了,由于我前几天刚做完杭州H,那就简单了,首先可以找到对应每一个数,它的左边离它最近的与它相同的数的位置,把它放入线段树这个数的位置,那么我们按照数值为主,位置为副的排序之后,然后就可以O(n)解决这个问题,然后我们对于每一个询问,我们询问区间最大值,如果最大值比左端点小,说明没有重复的,否则的话我们就直接找到最大值的位置的数就是从右往左数第一个重复的数。
AC代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
const int maxn = (int)5e5 + 10;
struct data{
int val, pos;
friend bool operator < (const data &a, const data &b){
if (a.val != b.val) return a.val < b.val;
else return a.pos < b.pos;
}
}x[maxn];
int mx[maxn << 2], X[maxn], n, m;
void PushUp(int rt){
mx[rt] = max(mx[rt << 1], mx[rt << 1 | 1]);
}
void build(int l, int r, int rt){
if (l == r){
mx[rt] = 0; return;
}
int m = (l + r) >> 1;
build(lson); build(rson);
PushUp(rt);
}
void update(int pos, int c, int l, int r, int rt){
if (l == r){
mx[rt] = c; return;
}
int m = (l + r) >> 1;
if (pos <= m) update(pos, c, lson);
else update(pos, c, rson);
PushUp(rt);
}
int query(int ll, int rr, int l, int r, int rt){
if (ll == l && rr == r) return mx[rt];
int m = (l + r) >> 1;
if (rr <= m) return query(ll, rr, lson);
else if (ll > m) return query(ll, rr, rson);
else return max(query(ll, m, lson), query(m + 1, rr, rson));
}
int main(){
while(~scanf("%d", &n)){
for (int i = 1; i <= n; i++){
scanf("%d", &X[i]);
x[i].val = X[i]; x[i].pos = i;
}
sort(x + 1, x + 1 + n);
build(1, n, 1);
for (int i = 1; i <= n; i++)
if (x[i].val == x[i - 1].val) update(x[i].pos, x[i - 1].pos, 1, n, 1);
scanf("%d", &m);
for (int i = 1; i <= m; i++){
int a, b;
scanf("%d%d", &a, &b);
int ans = query(a, b, 1, n, 1);
if (ans < a) puts("OK");
else printf("%d\n", X[ans]);
}
puts("");
}
return 0;
}