bzoj1878: [SDOI2009]HH的项链(主席树/离线+BIT)

    这题有离线和在线两种做法。

    离线:将查询区间按左端点排序,预处理出所有数下一次的出现位置,一开始将所有第一次出现的数a[i]++,之后当扫到这个数的时候a[next[i]]++,相当于差分,给之后的位置答案+1,因为查询区间左端点排序了,所以再也查不到当前点,这个数对答案有贡献的区间只有右端点在这个数下一次出现的位置右边的区间,当扫到查询区间左端点时当前答案为sum(r)-sum(l-1)。

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>
#include<map>
#define ll long long
using namespace std;
const int maxn=500010,inf=1e9;
struct poi{int l,r,pos;}q[maxn];
int n,m,x,y,z,tot,N;
int tree[maxn],a[maxn],b[maxn],ans[maxn],next[maxn],pre[maxn];
bool v[maxn];
inline void read(int &k)
{
    int f=1;k=0;char c=getchar();
    while(c<‘0‘||c>‘9‘)c==‘-‘&&(f=-1),c=getchar();
    while(c<=‘9‘&&c>=‘0‘)k=k*10+c-‘0‘,c=getchar();
    k*=f;
}
inline bool cmp(poi a,poi b){return a.l<b.l;}
inline int lowbit(int x){return x&-x;};
inline void add(int x){for(;x<=n;x+=lowbit(x))tree[x]++;}
inline int query(int x){int sum=0;for(;x;x-=lowbit(x))sum+=tree[x];return sum;}
int main()
{
    read(n);
    for(int i=1;i<=n;i++)read(a[i]),b[i]=a[i];N=n;
    sort(b+1,b+1+N);N=unique(b+1,b+1+N)-b-1;
    for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+1+N,a[i])-b;
    for(int i=n;i;i--)next[i]=pre[a[i]],pre[a[i]]=i;
    for(int i=1;i<=n;i++)
    if(!v[a[i]])add(i),v[a[i]]=1;
    read(m);
    for(int i=1;i<=m;i++)
    {
        read(q[i].l);read(q[i].r);
        q[i].l--;q[i].pos=i;
    }
    sort(q+1,q+1+m,cmp);
    for(int i=0,j=1;i<=n&&j<=m;i++)
    {
        if(next[i])add(next[i]);
        while(j<=m)
        {
            if(q[j].l!=i)break;
            ans[q[j].pos]=query(q[j].r)-query(q[j].l);
            j++;
        }
    }
    for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
    return 0;
}

    在线:预处理出所有数上一次出现的位置,把问题转化成查询区间里有多少数last[i]<l,这显然可以用主席树了

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>
#include<map>
#define ll long long
using namespace std;
const int maxn=50010,inf=1e9;
struct poi{int sum,lt,rt;}tree[maxn*20];
int n,m,l,r,sz,N;
int root[maxn],pre[maxn],last[maxn],a[maxn],b[maxn];
void read(int &k)
{
    int f=1;k=0;char c=getchar();
    while(c<‘0‘||c>‘9‘)c==‘-‘&&(f=-1),c=getchar();
    while(c<=‘9‘&&c>=‘0‘)k=k*10+c-‘0‘,c=getchar();
    k*=f;
}
void update(int &x,int l,int r,int cx)
{
    tree[++sz]=tree[x];tree[sz].sum++;x=sz;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(cx<=mid)update(tree[x].lt,l,mid,cx);
    else update(tree[x].rt,mid+1,r,cx);
}
int query(int x,int y,int l,int r,int cl,int cr)
{
    if(cl<=l&&r<=cr)return tree[y].sum-tree[x].sum;
    int mid=(l+r)>>1,ret=0;
    if(cl<=mid)ret+=query(tree[x].lt,tree[y].lt,l,mid,cl,cr);
    if(cr>mid)ret+=query(tree[x].rt,tree[y].rt,mid+1,r,cl,cr);
    return ret;
}
int main()
{
    read(n);
    for(int i=1;i<=n;i++)read(a[i]),b[i]=a[i];N=n;
    sort(b+1,b+1+N);N=unique(b+1,b+1+N)-b-1;
    for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+1+N,a[i])-b;
    for(int i=1;i<=n;i++)pre[i]=last[a[i]],last[a[i]]=i;
    for(int i=1;i<=n;i++)update(root[i]=root[i-1],0,n,pre[i]);
    read(m);
    for(int i=1;i<=m;i++)
    {
        read(l);read(r);
        printf("%d\n",query(root[l-1],root[r],0,n,0,l-1));
    }
}

时间: 2024-10-19 21:31:48

bzoj1878: [SDOI2009]HH的项链(主席树/离线+BIT)的相关文章

[bzoj1878] [SDOI2009]HH的项链(树状数组+离线)

1878: [SDOI2009]HH的项链 Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 3210  Solved: 1619[Submit][Status][Discuss] Description HH有一串由各种漂亮的贝壳组成的项链.HH相信不同的贝壳会带来好运,所以每次散步 完后,他都会随意取出一段贝壳,思考它们所表达的含义.HH不断地收集新的贝壳,因此, 他的项链变得越来越长.有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同 的

BZOJ 1878 HH的项链(主席树)

对于该题,离线的做法是树状数组或者线段树. 如果强制在线的话,可以用主席树做到O(mlogn). 考虑到这样一个性质,对于询问[l,r]出现的数字种数.其答案就是to[i]>r的数字数. 其中to[i]表示的是第i个数的下一个相同的数出现的下标,没有则=n+1. 很幸运这个性质是满足区间减法的,也就是说对于[1,r]和[1,l-1]的to[i]域,是可以相减得到[l,r]的to[i]域的. 于是我们可以用主席树来解决这个问题. 对于一组询问,实际上就是求[l-1,r]这颗线段树上的区间[r+1,

[luogu1972][bzoj1878][SDOI2009]HH的项链【莫队+玄学卡常】

题目大意 静态区间查询不同数的个数. 分析 好了,成功被这道题目拉低了AC率... 打了莫队T飞掉了,真的是飞掉了QwQ. 蒟蒻想不出主席树的做法,就换成了莫队... 很多人都不知道莫队是什么... 一句话概括莫队:离线询问分块排序,玄学降低复杂度 那么这道题目就是简单的莫队模板套一下就好了,每一次看看更新的点是不是会对答案造成贡献就可以过掉了. 但是复杂度很明显是\(Q(\sqrt{n}m)\),成功T掉,加上玄学卡常,破罐子破摔了100+终于过掉了. #include <bits/stdc+

【树状数组】Bzoj1878[SDOI2009] HH的项链

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

\BZOJ1878 [SDOI2009]HH的项链 树状数组 或 莫队

欢迎访问~原文出处--博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1878 题意概括 给出一个长度为n的序列,用m次询问,问区间Li~Ri中有多少种不同的数. 0<=数值<=1000000,n<=50000,m<=200000 题解 本题有许多做法. 这里介绍树状数组和莫队,都是离线算法. 树状数组 我们把序列按照R从小到大排序. 然后从左往右走. 依次加入数字,当前的状态,比如说搞定了前i个数字. 对于第i+1个数字,我们要给它做一个标记,但是不可

BZOJ1878 SDOI2009 HH的项链 树状数组

题意:给定一个颜色序列,每组询问给出区间[l,r],求[l,r]中不同颜色的数量 题解: 首先把所有颜色离散化,然后离线,将询问按右区间升序排列.从1-N把整个序列扫一遍,设Pos[i]为第i个颜色最后出现的位置,假定当前扫到的位置为i,则更新Pos[a[i]],那么问题变成了:求一个序列(Pos)中,大于等于一个数(L)的数的数量. 用树状数组维护Pos=j的数的数量,每次查询树状数组中L-N的和即可. 貌似SDOI不喜欢考大型数据结构啊……坐等今年打脸 #include <cstdio>

题解:bzoj1878: [SDOI2009]HH的项链

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

bzoj1878[SDOI2009]HH的项链

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1878 //通过此题,我粗略地了解了什么是离线和它的好处 题意:有一串颜色,m条询问:l,r,目的是求第l个数到第r个数中有多少个不同的数. 解析:(这是江湖上流传的一种做法,觉得非常有道理QAQ) 现将询问以l递增的顺序排列,设next[]为与当前颜色相同的下一位置,p[i]为第一个出现颜色i的位置, 开始时,将所有颜色的最初点x pre[x]++ 从左往右扫,每扫到x就将pre[next[

bzoj 1878: [SDOI2009]HH的项链【树状数组】

对于一个lr,每个颜色贡献的是在(1,r)区间里出现的最右位置,所以记录一个b数组表示当前点这个颜色上一个出现的位置 然后把询问离线,按r升序排序 每次把右端点右移,把这个点在树状数组上+1,并且在当前这个点的b位置上-1(表示没用了),然后树状数组前缀和减一下即可 我写的莫队会T #include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int N=100000