F. One Occurrence

https://codeforces.com/problemset/problem/1000/F

感觉可以用莫队草过去但是我不会写常数小的莫队+值域分块

看看tle 莫队code

// Problem: F. One Occurrence
// Contest: Codeforces - Educational Codeforces Round 46 (Rated for Div. 2)
// URL: https://codeforces.com/problemset/problem/1000/F
// Memory Limit: 768 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=5e5+9;
const int M=5e5+1;
int a[N];
int t,tt;
int L[N],R[N],pos[N];
int LL[M],RR[M],PP[M];
int c[M],vis[M];
int ans[N];
struct Q{
    int l,r,id;
    friend bool operator < (const Q &a,const Q &b){
        return pos[a.l]^pos[b.l]?pos[a.l]<pos[b.l]:pos[a.l]&1?a.r<b.r:a.r>b.r;
    }
}que[N];
int query(){
    int ans=0;
	for(int i=1;i<=PP[M-1];i++){
		if(vis[i]){
			for(int j=LL[i];j<=RR[i];j++){
				if(c[j]==1){
					ans=j;
					break;
				}
			}
		}
	}
    return ans;
}
void modify(int pos,int val){
    if(val==1){
        if(c[pos]==1){
            vis[PP[pos]]--;
        }
        if(c[pos]==0){
            vis[PP[pos]]++;
        }
    }else{
        if(c[pos]==1){
            vis[PP[pos]]--;
        }
        if(c[pos]==2){
            vis[PP[pos]]++;
        }
    }
    c[pos]+=val;
}
void add(int pos){
    modify(a[pos],1);
}
void del(int pos){
    modify(a[pos],-1);
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    t=sqrt(n);
    for(int i=1;i<=t;i++){
        L[i]=(i-1)*t+1;
        R[i]=i*t;
    }
    if(R[t]<n){
        t++;
        L[t]=R[t-1]+1;
        R[t]=n;
    }
    for(int i=1;i<=t;i++){
        for(int j=L[i];j<=R[i];j++){
            pos[j]=i;
        }
    }
    tt=sqrt(M);
    for(int i=1;i<=tt;i++){
        LL[i]=(i-1)*tt+1;
        RR[i]=i*tt;
    }
    if(RR[tt]<M){
        tt++;
        LL[tt]=RR[tt-1]+1;
        RR[tt]=M;
    }
    for(int i=1;i<=tt;i++){
        for(int j=LL[i];j<=RR[i];j++){
            PP[j]=i;
        }
    }
    int q;
    cin>>q;
    for(int i=1;i<=q;i++){
        cin>>que[i].l>>que[i].r;
        que[i].id=i;
    }
    sort(que+1,que+1+q);
    int l=1,r=0;
    for(int i=1;i<=q;i++){
        while(que[i].l>l)del(l++);
        while(que[i].l<l)add(--l);
        while(que[i].r>r)add(++r);
        while(que[i].r<r)del(r--);
        ans[que[i].id]=query();
    }
    for(int i=1;i<=q;i++){
        cout<<ans[i]<<'\n';
    }
    return 0;
}

 接下来是ac的主席树

很像HH的项链,维护上一个相同的值的位置

我们维护上一个相同值的位置,维护区间最小值,如果最小值小于l,就有说明有[l,r]有一个值的数量是1

为了防止第一个出现的值影响答案,我们还要把第一个出现的值修改成INF(原本是0)

这样就可以在线操作完

因为每个数有可能修改2次,把N要再开大2倍

// Problem: F. One Occurrence
// Contest: Codeforces - Educational Codeforces Round 46 (Rated for Div. 2)
// URL: https://codeforces.com/problemset/problem/1000/F
// Memory Limit: 768 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<iostream>
using namespace std;
const int N=5e5+9;
int a[N],vis[N],last[N];
//可持久化线段树
struct KCJSEG{
    #define tl(id) seg[id].l
    #define tr(id) seg[id].r
    #define ll long long
    #define INF 1e6
    struct node{
    	int L,R;
        int l,r;
        int mn,index;
    }seg[N<<6];
    void pushup(node &id,node &l,node &r){
    	if(l.mn>r.mn){
    		id.mn=r.mn;
    		id.index=r.index;
    	}else{
    		id.mn=l.mn;
    		id.index=l.index;
    	}
    }
	void pushup(int id){pushup(seg[id],seg[tl(id)],seg[tr(id)]);}
    int root[N],index,mx;
    int inrange(int L,int R,int l,int r){return L>=l && R<=r;}
    int outofrange(int L,int R,int l,int r){return L>r || l>R;}
    void build(int &id,int l,int r){
        id=++index;
        seg[id]={l,r};
        if(l==r){
            seg[id].mn=INF;
            seg[id].index=l;
            return;
        }
        int mid=(l+r)>>1;
        build(tl(id),l,mid);
        build(tr(id),mid+1,r);
        pushup(id);
    }
    void update(int post,int &curr,int l,int r,int pos,int v){
        curr=++index;
        seg[curr]=seg[post];
        if(l==r){
            seg[curr].mn=v;
            return;
        }
        int mid=(l+r)>>1;
        if(mid>=pos){
            update(tl(post),tl(curr),l,mid,pos,v);
        }else{
            update(tr(post),tr(curr),mid+1,r,pos,v);
        }
        pushup(curr);
    }
    node query(int curr,int l,int r){
    	if((seg[curr].L>=l && seg[curr].R<=r)){
    		return seg[curr];
    	}else{
    		int mid=(seg[curr].L+seg[curr].R)>>1;
    		if(mid>=r){
    			return query(tl(curr),l,r);
    		}else if(mid<l){
    			return query(tr(curr),l,r);
    		}else{
    			node res;
    			node left=query(tl(curr),l,r);
    			node right=query(tr(curr),l,r);
    			pushup(res,left,right);
    			return res;
    		}
    	}
    }
}t;
#define node KCJSEG::node
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	int n;
	cin>>n;
	t.build(t.root[0],1,n);
	for(int i=1;i<=n;i++){
		cin>>a[i];
		t.root[i]=t.root[i-1];
		if(vis[a[i]]){
			t.update(t.root[i],t.root[i],1,n,vis[a[i]],INF);
			last[a[i]]=vis[a[i]];
			t.update(t.root[i],t.root[i],1,n,i,last[a[i]]);
			vis[a[i]]=i;
		}else{
			vis[a[i]]=i;
			t.update(t.root[i],t.root[i],1,n,i,0);
		}
	}
	int q;
	cin>>q;
	for(int i=1;i<=q;i++){
		int l,r;
		cin>>l>>r;
		node ans=t.query(t.root[r],l,r);
		if(ans.mn>=l){
			cout<<0<<'\n';
		}else{
			cout<<a[ans.index]<<'\n';
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值