题意:给一串数字,每次区间查询,输出从右往左第一个重复的数字,如果没有重复数字则输出"OK"。
思路:据说此题暴力也能过,但应该不是正解。我的思路是用离线处理,先把所有查询读入,并保存为区间,根据区间右端点存进相应的vector数组中(区间右端点作为数组索引)。这样从右往左遍历所有的数字,并用一个queue(队列)来保存所有经过的查询区间。这样,一旦经过某个结点就将以该位置为右端点的查询区间存入队列。对于每个位置,维护它上次出现的位置,这样判断该数字这次出现的位置和上次出现的位置是否都在队首的查询区间中。如果是则可以确定该查询区间的答案就是这个数字,如果不是那么就继续遍历左边的数字。考虑一种情况,如果当前位置已经位于队首区间左端点的左边了,可以确定该区间一定是OK的。最后要记得遍历队列,处理所有OK的情况。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#include <queue>
using namespace std;
typedef long long LL;
struct Segment
{
int num,l,r,ans;
Segment(int nn=0,int ll=0,int rr=0,int aa=0):num(nn),l(ll),r(rr),ans(aa)
{
}
bool operator <(const Segment &rhs) const
{
return num<rhs.num;
}
};
const int maxn=500005;
int a[maxn];
vector<Segment> seg[maxn];
vector<Segment> ans;
bool judge(int l1,int l2,int r2,int r1)
{
return l1<=l2&&r2<=r1;
}
int main()
{
int n,m;
while(scanf("%d",&n)!=EOF)
{
for(int i=1; i<=n; ++i)
scanf("%d",&a[i]);
scanf("%d",&m);
for(int i=1; i<=n; ++i)
seg[i].clear();
for(int i=1; i<=m; ++i)
{
int l,r;
scanf("%d%d",&l,&r);
seg[r].push_back(Segment(i,l,r,0));
}
queue<Segment> que;
map<int,int> lastVis;
ans.clear();
for(int i=n; i>=1; --i)
{
for(int j=0; j<seg[i].size(); ++j)
que.push(seg[i][j]);
int vis=lastVis[a[i]];
if(vis!=0)
{
while(!que.empty())
{
if(i<que.front().l)
{
ans.push_back(que.front());
que.pop();
}
else if(judge(que.front().l,i,vis,que.front().r))
{
Segment s=que.front();
que.pop();
s.ans=i;
ans.push_back(s);
}
else
break;
}
}
lastVis[a[i]]=i;
}
while(!que.empty())
{
ans.push_back(que.front());
que.pop();
}
sort(ans.begin(),ans.end());
for(int i=0; i<ans.size(); ++i)
{
if(ans[i].ans==0) puts("OK");
else printf("%d\n",a[ans[i].ans]);
}
printf("\n");
}
return 0;
}