One Occurrence CodeForces - 1000F (思维+主席树)

题目链接

题意:给定序列a ,q次询问 每次询问要求[l,r]区间任意一个只出现过一次的数 没有则输出0 。

分析:这题和这题十分类似,

我们先分析对于右端点固定的区间,即:求出[1,r]的某个[l,r]区间只出现过一次的数。

我们用last[i]维护i上一次出现的位置,那么对于序列last[a[i]]
(i映射成last[a[i]]),我们查询[l,r]内是否存在只出现过一次的数就等价于last[a[i]]在[l,r]区间是否存在小于l的值, 根据贪心我们可以用线段树维护序列last[a[i]]的区间最小值。 每次询问就查询线段树[l,r]内最小值是否小于l即可。

这题右端点不固定,我们可以对每个前缀建线段树。每次询问取出r版本线段树进行查询即可。

用动态开点维护最小值的时候,可以统一把虚点设成INF。然后正常的pushup就行。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 5e5+5;
const int mx = 40;
const int mod = 1e9+5;
const ll inf = 34359738370;
const int INF = 1e9+5;
//给定序列a ,q次询问 每次询问要求[l,r]区间任意一个只出现过一次的数  没有则输出0 
//和求子区间不同种类个数那题是差不多的技巧
//last[i]表示i上一次出现的位置 线段树维护区间内的数上一次出现的最远位置 第一次出现则为0
//固定右端点的话按右端点排序 离线查询
//每次询问 查询的时候查root[r]这棵树的[l,r]区间内的最小值 如果小于l就有解
struct node
{
    int v,index;//区间内的数上一次出现最远的位置  这个数的位置
/*    friend bool operator<(const node &a,const node&b)
    {
        if(a.v != b.v) return a.v<b.v;
        else return a.index<b.index;
    }*/
    bool operator < (const node &a)const
    {
        if(v != a.v) return v<a.v;
        else return index<a.index;
    }
}tree[maxn*mx];
int lc[maxn*mx],rc[maxn*mx],cnt=0,root[maxn];
int last[maxn];
void updata(int &rt,int last,int l,int r,int pos,int v)//pos位置的值设成v
{
    rt=++cnt;
    tree[rt]=tree[last];
    lc[rt]=lc[last],rc[rt]=rc[last];
    if(l == r) 
    {
        tree[rt].index=pos;
        tree[rt].v=v;
        return ;
    }
    int mid=(l+r)>>1;
    if(pos<=mid) updata(lc[rt],lc[last],l,mid,pos,v);
    else updata(rc[rt],rc[last],mid+1,r,pos,v);
    tree[rt]=min(tree[lc[rt]],tree[rc[rt]]);
}
node query(int rt,int l,int r,int vl,int vr)//第rt棵树 [vl,vr]内的最小值
{
    if(vl<=l && r<=vr) return tree[rt];
    int mid=(l+r)>>1;
    if(vr<=mid) return query(lc[rt],l,mid,vl,vr);
    else if(vl>mid) return query(rc[rt],mid+1,r,vl,vr);
    node t1=query(lc[rt],l,mid,vl,vr),t2=query(rc[rt],mid+1,r,vl,vr);
    return min(t1,t2);
}
int n,m,a[maxn];
int main()
{
	tree[0].v=INF;//把所有的虚点设成INF
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",a+i);
        if(!last[a[i]])
        {
            updata(root[i],root[i-1],1,n,i,0);
        }
        else 
        {
            int temp;
            updata(temp,root[i-1],1,n,last[a[i]],INF);//设成INF  消除上一个位置对当前位置的影响 
            updata(root[i],temp,1,n,i,last[a[i]]);
        }
        last[a[i]]=i;
    }
    scanf("%d",&m);
    while(m--)
    {
        int l,r;
        scanf("%d %d",&l,&r);
        node x=query(root[r],1,n,l,r);
        if(x.v >= l) puts("0");
        else printf("%d\n",a[x.index]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值