天天看点

洛谷 P1972 [SDOI2009]HH的项链洛谷 P1972 [SDOI2009]HH的项链 主席树+区间不同数字个数

洛谷 P1972 [SDOI2009]HH的项链 主席树+区间不同数字个数

😊 | Powered By HeartFireY

Problem Analysis

题目大意:给定 n n n个数的序列,以及随后的 m m m组询问。回答对于每组询问给出的 l , r l,r l,r,区间 [ l , r ] [l,r] [l,r]内不同数字的个数。

首先问题的形式与主席树是十分相似的:先给定序列,然后给定多组区间查询。但是显然不是单纯的维护区间第 k k k大的问题。

那么考虑如何将问题转化为主席树可维护的问题:我们对每个点维护一个数组元素 n x t [ i ] nxt[i] nxt[i],表示最近的下一个同色点的下标。那么现在不难发现对于给定区间 [ l , r ] [l,r] [l,r],如果 n x t [ i ] > r ,   i ∈ [ l , r ] nxt[i] > r,\ i \in [l,r] nxt[i]>r, i∈[l,r],那么表示与 i i i相同颜色点位于区间之外。那么求不同颜色问题便转化为给定求所有满足 l < = i < = r ,   n x t [ i ] > r l<=i<=r,\ nxt[i]>r l<=i<=r, nxt[i]>r的个数。

那么我们只需要处理出 n x t nxt nxt数组,然后用主席树对每个节点维护权值数组,然后区间查询数目即可。

Accepted Code

#include <bits/stdc++.h>
using namespace std;

const int N = 1e6 + 10;
int a[N], head[N], nxt[N];
int root[N], sum[N << 5], lc[N << 5], rc[N << 5], cnt;

inline int read(){
    int f = 1, x = 0; char s = getchar(); 
    while(s < '0'||s > '9'){ if(s =='-') f = -1; s = getchar(); } 
    while(s >= '0' && s <= '9'){ x = x * 10 + s - '0'; s = getchar();}
    return x *= f; 
}

void build(int &rt, int l, int r){
    rt = ++cnt;
    if(l == r) return;
    int mid = l + r >> 1;
    build(lc[rt], l, mid);
    build(rc[rt], mid + 1, r);
}

void update(int &rt, int pre, int l, int r, int x){
    rt = ++cnt, lc[rt] = lc[pre], rc[rt] = rc[pre], sum[rt] = sum[pre] + 1;
    if(l == r) return;
    int mid = l + r >> 1;
    if(x <= mid) update(lc[rt], lc[pre], l, mid, x);
    else update(rc[rt], rc[pre], mid + 1, r, x);
}

int query(int L, int R, int l, int r, int k){
    if(l == r) return sum[R] - sum[L];
    int mid = l + r >> 1, ans = 0;
    if(k <= mid) ans += query(lc[L], lc[R], l, mid, k) + sum[rc[R]] - sum[rc[L]];
    else ans += query(rc[L], rc[R], mid + 1, r, k);
    return ans;
}

signed main(){
    //ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int n = 0; //cin >> n;
    n = read();
    for(int i = 1; i <= n; i++){
        a[i] = read();  //cin >> a[i];
        if(head[a[i]]) nxt[head[a[i]]] = i;
        head[a[i]] = i;
    }
    for(int i = 1; i <= n; i++)
        if(!nxt[i]) nxt[i] = n + 1;
    
    build(root[0], 1, n + 1);
    for(int i = 1; i <= n; i++) update(root[i], root[i - 1], 1, n + 1, nxt[i]);

    int m = read();
    while(m--){
        int l = read(), r = read(); //cin >> l >> r;
        //cout << query(root[l - 1], root[r], 1, n + 1, r + 1) << endl;
        printf("%d\n", query(root[l - 1], root[r], 1, n + 1, r + 1));
    }
    
    return 0;
}
           

继续阅读