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

题目背景

题目描述

HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH 不断地收集新的贝壳,因此,他的项链变得越来越长。有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答……因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。

输入输出格式

输入格式:

第一行:一个整数N,表示项链的长度。

第二行:N 个整数,表示依次表示项链中贝壳的编号(编号为0 到1000000 之间的整数)。

第三行:一个整数M,表示HH 询问的个数。

接下来M 行:每行两个整数,L 和R(1 ≤ L ≤ R ≤ N),表示询问的区间。

输出格式:

M 行,每行一个整数,依次表示询问对应的答案。

输入样例#1:

6
1 2 3 4 3 5
3
1 2
3 5
2 6

输出样例#1:

2
2
4

说明

数据范围:

对于100%的数据,N <= 500000,M <= 200000。

思路

给出一个有n个数的序列,然后给出m组询问,每组询问一个区间 [ l , r ] ,求这个区间内包含多少种不同的数。

主席树可以离线处理,在线查询,就是不需要把询问都读进来,而是可以及时回答每一个询问。当输入数据被根据上一次查询的答案加密过后,莫队和离线操作就无用武之地了。这时候我们可以考虑转化问题。

对于每一个点,都制作一个next[i]表示在这个点之后最近的颜色相同的点,如果没有就设为n+1,记一下队头 O ( N ) 扫一遍就好了

考虑区间查询l~r之间的颜色种数,其实就是求所有满足(l<=i<=r,next[i]>r)的个数,因为如果某个点的next已近超出了这个区间的范围,就说明这个点对答案产生贡献了。

这个时候问题就已近被转化为给定一个序列,求区间l~r之间权值大于r的个数。

那么我们对于每个点都在可持久化的权值线段树中构造一条新的线段树链就好了,查询就是常规的权值线段树的查询。

对于每个点都要新建一条最多 l o g 2 N 个点的链,空间复杂度 n l o g 2 n ;对于每次询问最多递归深度为 l o g 2 n 层,时间复杂度 m l o g 2 n

代码

#include <bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long ll;
const int N=1e6+10;
int n,m,p,node_cnt;
int head[N],nex[N],a[N];
int rt[N],lc[N<<5],rc[N<<5],sum[N<<5];
void build(int &t,int l,int r)
{
    t=++node_cnt;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(lc[t],l,mid);
    build(rc[t],mid+1,r);
}
int modify(int o,int l,int r)
{
    int oo=++node_cnt;
    lc[oo]=lc[o];
    rc[oo]=rc[o];
    sum[oo]=sum[o]+1;
    if(l==r) return oo;
    int mid=(l+r)>>1;
    if(p<=mid)
        lc[oo]=modify(lc[oo],l,mid);
    else
        rc[oo]=modify(rc[oo],mid+1,r);
    // sum[oo] = sum[lc[oo]] + sum[rc[oo]];
    return oo;
}
//u,v代表当前节点的rt
int query(int u,int v,int l,int r,int k)//返回区间u到v,>=k的个数
{
    int mid=(l+r)>>1,ans=0;
    if(l==r) return sum[v]-sum[u];
    if(k<=mid)
    {
        int x=sum[rc[v]]-sum[rc[u]];
        ans+=query(lc[u],lc[v],l,mid,k)+x;
    }
    else
        ans+=query(rc[u],rc[v],mid+1,r,k);
    return ans;
}
void init()
{
    mem(head,0);
    mem(nex,0);
    node_cnt=0;
}
int main()
{
    scanf("%d",&n);
    init();
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&a[i]);
        if(head[a[i]])
            nex[head[a[i]]]=i;
        head[a[i]]=i;
    }
    for(int i=1; i<=n; i++)
        if(!nex[i])
            nex[i]=n+1;
    build(rt[0],1,n+1);
    for(int i=1; i<=n; i++)
    {
        p=nex[i];
        rt[i]=modify(rt[i-1],1,n+1);
    }
    int l,r;
    scanf("%d",&m);
    while(m--)
    {
        scanf("%d%d",&l,&r);
        printf("%d\n",query(rt[l-1],rt[r],1,n+1,r+1));
    }
    return 0;
}
/*
6
1 2 3 4 3 5
3
2 6
*/

猜你喜欢

转载自blog.csdn.net/riba2534/article/details/81354214