ZOJ:3633 Alice's present(离线处理)

题意:给一串数字,每次区间查询,输出从右往左第一个重复的数字,如果没有重复数字则输出"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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值