【区间不同数的个数 && (主席树 || 莫队)】SPOJ - DQUERY D-query

Step1 Problem:

给你 n 个数和 q 个询问,每次询问 问你 [L, R] 不同数的个数。

Step2 Code:

主席树:
1:每一颗线段树区间维护的是不同数的个数。
2:每一颗线段树维护的值,是最后出现的。例如第 4 棵线段树,维护时只要第 4 个 1,前两个 1 不要。
对于询问 [L, R] 我们只需要第 R 棵线段树,直接求 [L, R] 范围内不同数的个数。
对于第 2 点如何解决,如果第 i 个数,在前面已经出现过,那么我们先删除第 i 个数,然后在插入。

Step3 Code:

#include<bits/stdc++.h>
using namespace std;
#define MID int mid = (l+r)/2
const int N = 3e4+5;
struct node
{
    int sum, l, r;
}tree[N*80];
int vis[1000005], a[N], root[N], top = 0;
int creat(int sum, int l, int r)
{
    tree[top++] = (node){sum, l, r};
    return top-1;
}
void updata(int &root, int pre, int l, int r, int pos, int v)
{
    root = creat(tree[pre].sum+v, tree[pre].l, tree[pre].r);
    if(l == r) return ;
    MID;
    if(pos <= mid) updata(tree[root].l, tree[pre].l, l, mid, pos, v);
    else updata(tree[root].r, tree[pre].r, mid+1, r, pos, v);
}
int query(int root, int l, int r, int k)//区间大于 k 我们才要。
{
    if(l == r) return tree[root].sum;
    MID;
    int ret = 0;
    if(k <= mid) ret += tree[tree[root].r].sum + query(tree[root].l, l, mid, k);
    else ret += query(tree[root].r, mid+1, r, k);
    return ret;
}
int main()
{
    int n, m;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
        scanf("%d", a+i);
    scanf("%d", &m);
    root[0] = creat(0, 0, 0);
    memset(vis, 0, sizeof(vis));
    int tmp;
    for(int i = 1; i <= n; i++)
    {
        if(!vis[a[i]]) {//前面没出现过,直接插入
            updata(root[i], root[i-1], 1, n, i, 1);
        }
        else {//如果前面出现过,先删除,在插入
            updata(tmp, root[i-1], 1, n, vis[a[i]], -1);
            updata(root[i], tmp, 1, n, i, 1);
        }
        vis[a[i]] = i;
    }
    int ul, ur;
    while(m--)
    {
        scanf("%d %d", &ul, &ur);
        printf("%d\n", query(root[ur], 1, n, ul));//直接求[ul, ur]范围内不同数的个数
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/bbbbswbq/article/details/80990146
今日推荐