[CF1000F] One Occurrence(线段树)

本文介绍如何使用线段树数据结构解决查询区间内只出现一次的数字的问题,并提供了一个具体的实现方案及源代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

我是超链接

题意:

不强制在线,询问q个区间内只出现一次的数字,输出这个数字(任意一个)

题解:

一棵线段树就行了,从左往右依次把每个位置的数字&pre的位置加上,线段树的节点表示位置
考虑以i为右节点,左节点为l(很多个)的情况。
我们线段树维护每个节点的pre的min,如果min < l,说明这个数字在l~r中只出现了一次,就是个合法的数字,那么我们区间查询合法数字就行
但是如果这种情况呢?这里写图片描述
2的pre在l的左端,但是3还出现了,就不是个合法的情况
怎么避免呢?
可以选择在加入3的时候,如果他的pre存在(2),就把pre这个位置的值去掉就可以了,设为INF

这样就是一个单点修改,区间查询的线段树了

代码:

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define INF 1e9
const int N=500005;
int ans[N],a[N],pre[N],last[N];
struct hh{int pre,x;hh(int X=0,int Y=0){pre=X; x=Y;}}tree[N*4],hx;
vector<hh>q[N];
bool operator <(hh a,hh b){return a.pre<b.pre;}
void updata(int now){tree[now]=min(tree[now<<1],tree[now<<1|1]);}
void change(int now,int l,int r,int x)
{
    if (l==r){tree[now]=hx;return;}
    int mid=(l+r)>>1;
    if (x<=mid) change(now<<1,l,mid,x);
    else change(now<<1|1,mid+1,r,x); 
    updata(now);
}
void qurry(int now,int l,int r,int lrange,int rrange)
{
    if (lrange<=l && rrange>=r) {hx=min(hx,tree[now]);return;}
    int mid=(l+r)>>1;
    if (lrange<=mid) qurry(now<<1,l,mid,lrange,rrange);
    if (rrange>mid) qurry(now<<1|1,mid+1,r,lrange,rrange);
}
int main()
{
    int n,Q;scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    scanf("%d",&Q);
    for (int i=1;i<=Q;i++)
    {
        int x,y;scanf("%d%d",&x,&y);
        q[y].push_back((hh){x,i});
    }
    for (int i=1;i<=n;i++)
    {
        pre[i]=last[a[i]];
        last[a[i]]=i;
    }
    for (int i=1;i<=n;i++)
    {
        if (pre[i])
          {hx=hh(INF,INF);change(1,1,n,pre[i]);}
        hx=hh(pre[i],a[i]);
        change(1,1,n,i);
        for (int j=0;j<q[i].size();j++)
        {
            hx=hh(INF,INF);
            qurry(1,1,n,q[i][j].pre,i);
            if (hx.pre<q[i][j].pre) ans[q[i][j].x]=hx.x;
            else ans[q[i][j].x]=0;
        }
    }
    for (int i=1;i<=Q;i++) printf("%d\n",ans[i]);
}
调一下代码(CF1000F One Occurrence,WA了): #include<bits/stdc++.h> using namespace std; int n ,q, a[500010], bl[500010], kuai_len; struct node{ int l, r, tim; friend bool operator <(node t1, node t2){ return bl[t1.l] == bl[t2.l] ? (t1.r == t2.r ? 0 : (bl[t1.l] & 1) ^ (t1.r > t2.r)) : t1.l < t2.l; } }b[500010]; int cnt, sum[500010], s[1010], ans[500010]; int js(){ if(cnt == 0) return 0; for(int i = 0; i <= 800; ++ i){ if(s[i]){ for(int j = i * kuai_len; j <= (i + 1) * kuai_len - 1; ++ j){ if(sum[j] == 1) return j; } } } } int main(){ scanf("%d", &n); kuai_len = pow(n, 0.5); for(int i = 1; i <= n; ++ i){ scanf("%d", &a[i]); bl[i] = (i - 1) / kuai_len + 1; } scanf("%d", &q); for(int i = 1; i <= q; ++ i){ scanf("%d %d", &b[i].l, &b[i].r); b[i].tim = i; } sort(b + 1, b + q + 1); int pl = 1, pr = 0; for(int i = 1; i <= q; ++ i){ while(pl > b[i].l){ -- pl; if(sum[a[pl]] == 1) s[a[pl] / kuai_len] --, -- cnt; else if(sum[a[pl]] == 0) s[a[pl] / kuai_len] ++, ++ cnt; ++ sum[a[pl]]; } while(pr < b[i].r){ ++ pr; if(sum[a[pr]] == 1) s[a[pr] / kuai_len] --, -- cnt; else if(sum[a[pr]] == 0) s[a[pr] / kuai_len] ++, ++ cnt; ++ sum[a[pr]]; } while(pl < b[i].l){ -- sum[a[pl]]; if(sum[a[pl]] == 1) s[a[pl] / kuai_len] ++, ++ cnt; else if(sum[a[pl]] == 0) s[a[pl] / kuai_len] --, -- cnt; ++ pl; } while(pr > b[i].r){ -- sum[a[pr]]; if(sum[a[pr]] == 1) s[a[pr] / kuai_len] ++, ++ cnt; else if(sum[a[pr]] == 0) s[a[pr] / kuai_len] --, -- cnt; -- pr; } ans[b[i].tim] = js(); } for(int i = 1; i <= q; ++ i){ printf("%d\n", ans[i]); } return 0; }
03-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值