BZOJ 1878 [SDOI2009]HH的项链——离线+树状数组||莫队算法

1878: [SDOI2009]HH的项链

题目描述

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

输入

第一行:一个整数N,表示项链的长度。
第二行:N个整数,表示依次表示项链中贝壳的编号(编号为0到1000000之间的整数)。
第三行:一个整数M,表示HH询问的个数。
接下来M行:每行两个整数,L和R(1 ≤ L ≤ R ≤ N),表示询问的区间。N ≤ 50000,M ≤ 200000。

输出

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

解题思路

方法一
我想各位dalao一看到这种多次查询一个区间的题目就会马上想到莫队算法,没错这题可以用莫队水过,下面附上弱(chu)弱(nv)的(zuo)代码。

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=50005,maxm=200005,maxv=1000005;
struct jz{
    int id,L,R;
}q[maxm];
int h[maxn],n,m,a[maxn],sum[maxv],ans[maxm],tot;
bool cmp(jz a,jz b){
    if (h[a.L]==h[b.L]) return a.R<b.R;
    return a.L<b.L;
}
inline int _read(){
    int num=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') num=num*10+ch-48,ch=getchar();
    return num;
}
void move(int x,int y){
    if (y==1&&sum[a[x]]==0) tot++;
    if (y==-1&&sum[a[x]]==1) tot--;
    sum[a[x]]+=y; 
}
int main(){
    freopen("exam.in","r",stdin);
    freopen("exam.out","w",stdout);
    n=_read();for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    m=_read();for (int i=1;i<=m;i++) scanf("%d%d",&q[i].L,&q[i].R),q[i].id=i;
    int k=sqrt(n);for (int i=1;i<=n;i++) h[i]=(i-1)/k+1;
    sort(q+1,q+1+m,cmp);
    int L=1,R=1;move(1,1);
    for (int i=1;i<=m;i++){
        while (L>q[i].L) move(--L,1);
        while (L<q[i].L) move(L++,-1);
        while (R<q[i].R) move(++R,1);
        while (R>q[i].R) move(R--,-1);
        ans[q[i].id]=tot;
    }
    for (int i=1;i<=m;i++) printf("%d\n",ans[i]);
    return 0;
}

方法二
还有一种比较巧妙的方法就是离线所有询问,使起点递增,用k表示当前起点,显然处在[k,n]每种贝壳的第一个才是值得我们关注的,将这些点标为1,其余为0,种类数就是到k的前缀和(用树状数组能方便求出)。如果起点需要移动,就把nxt[a[i]]标为1(nxt数组可以提前构造)。

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=50005,maxm=1000005;
int ans[maxn*4],a[maxn],n,m,nxt[maxn],w[maxm],x[maxn],mx;
struct jz{
    int x,y,w;
    bool operator<(const jz&b)const{
        return x<b.x;
    }
}b[maxn*4];
inline int _read(){
    int num=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') num=num*10+ch-48,ch=getchar();
    return num;
}
int lowbit(int x){return x&(-x);}
void change(int x,int y){
    while (x<=n){
        a[x]+=y;
        x+=lowbit(x);
    }
}
int ask(int x){
    int num=0;
    while (x>0){
        num+=a[x];
        x-=lowbit(x);
    }
    return num;
}
int main(){
    freopen("exam.in","r",stdin);
    freopen("exam.out","w",stdout);
    n=_read();
    for (int i=1;i<=n;i++) x[i]=_read();
    for (int i=n;i>=1;i--){
        if (w[x[i]]!=0) nxt[i]=w[x[i]];
        w[x[i]]=i;mx=max(x[i],mx);
    }
    m=_read();
    for (int i=1;i<=m;i++) b[i].x=_read(),b[i].y=_read(),b[i].w=i;
    sort(b+1,b+1+m);
    for (int i=1;i<=mx;i++) if (w[i]!=0) change(w[i],1);b[0].x=1;
    for (int i=1;i<=m;i++){
        for (int j=b[i-1].x;j<=b[i].x-1;j++) if (nxt[j]!=0) change(nxt[j],1),change(j,-1);
        ans[b[i].w]=ask(b[i].y)-ask(b[i].x-1);
    }
    for (int i=1;i<=m;i++) printf("%d\n",ans[i]);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值