刷题记录luoguP1972 [SDOI2009]HH的项链

来源:互联网 发布:java ssh jar 编辑:程序博客网 时间:2024/04/26 20:36

这题是典型的区间处理问题

由于数据规模,我们可以采用离线操作:先将询问区间保存下来,对区间进行合理的排序,便于降低复杂度

然后采用合适的算法

(1)树状数组

我们可以给相应元素一个权值1,用树状数组计算sum,

但这题显然一般的计算是行不通的,

考虑到区间的有序性,我们以区间左端点排序,

然后依次处理,这样对于区间i+1左端点一定不会在区间i左端点之前

那么我们处理到区间i的时候,i左端点之前的都是废物,可以扔掉,反正后面又不会用到

具体怎么做呢?

设位置i的对应值是a[i],那么记录下a[i]出现的下个位置,不妨保存在next数组里面

举个栗子:

a       1 2 2 4 3 2

next  0 3 6 0 0 0

好的,我们已经处理的区间左端点是j,现在要处理k了

对于j到k之间的所有元素,统统扔掉,update(相应位置,-1)

然后一直next,直到大于等于k,update(相应位置,+1)。

举个栗子:

a       1 2 2 4 3 2

next  0 3 6 0 0 0

1 5已处理

正准备处理4 5

[1,4)直接干掉

1next是0,直接update(1,-1)

2next是3,update(2,-1)

不行啊,3还是小于4 继续next 为6 update(3,-1) update(6,1)

同理继续搞就是了

这样就可以用树状数组求和了

这个方法主要是利用了离线的方法,通过合理调整顺序,以到达降低复杂度的效果。

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<algorithm>
#define pii pair<int,int>
#define MAXN 50005
#define MAXM 200005
#define MAXS 1000005
using namespace std;
struct Ask{
    int L,R;
    int num;
    int ans;
    Ask(){
        L=R=num=ans=0;
    }
};
int dat[MAXN];
int a[MAXN],b[MAXN];
int nxt[MAXN],lst[MAXS];
int fst[MAXN];
Ask p[MAXM];
int n,m;
bool comp1(const Ask &p1,const Ask &p2){
    return (p1.L<p2.L);
}
bool comp2(const Ask &p1,const Ask &p2){
    return (p1.num<p2.num);
}
void update(int k,int x){
    while(k<=n){
        dat[k]+=x;
        k+=(k&-k);
    }
}
int sum(int k){
    int ret=0;
    while(k>=1){
        ret+=dat[k];
        k-=(k&-k);
    }
    return ret;
}
int main()
{
//    freopen("data.in","r",stdin);
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        if(lst[a[i]]){
            int x=lst[a[i]];
            nxt[x]=i;
        }
        else{
            fst[i]=1;
        }
        lst[a[i]]=i;
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&p[i].L,&p[i].R);
        p[i].num=i;
    }
    sort(p+1,p+m+1,comp1);
    for(int i=1;i<=n;i++){
        if(fst[i]){
            b[i]=1;
            update(i,1);
        }
    }
    int temp=1;
    for(int i=1;i<=m;i++){
        int L=p[i].L,R=p[i].R;
        for(int j=temp;j<L;j++){
            b[j]=0;
            update(j,-1);
            int cnt=j;
            while(cnt<L&&cnt){
                cnt=nxt[cnt];
            }
            if(cnt){
                b[cnt]=1;
                update(cnt,1);
            }
        }
        p[i].ans=sum(R)-sum(L-1);
//        printf("%d\n",ans);
        temp=L;
    }
    sort(p+1,p+m+1,comp2);
    for(int i=1;i<=m;i++){
        printf("%d\n",p[i].ans);
    }
    return 0;
}

(2)莫队算法

莫队算法适用条件:已知[L,R]求出[L+/-1,R]或者是[L,R+/-1]很容易就可以啦

这题显然是很容易求出来的,我们定义一个数组b,记录[L,R]次数

当新加进去的元素或者新跑出来的元素Z,使得b[Z]变得非零或者变成零,那么ans++或者ans--即可

没学过的同学可以百度莫队算法

其实它的思想也是通过离线来调整区间顺序,使得效率最大化的

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define MAXM 200005
#define MAXN 50005
#define MAXS 1000005
using namespace std;
struct Ask{
    int L,R;
    int id;
    int ans;
};
Ask qu[MAXM];
int n,m;
int a[MAXN],pos[MAXN];
int len;
int b[MAXS];
bool comp1(const Ask &q1,const Ask &q2){
    if(pos[q1.L]==pos[q2.L]){
        return (q1.R<q2.R);
    }
    else{
        return (pos[q1.L]<pos[q2.L]);
    }
}
bool comp2(const Ask &q1,const Ask &q2){
    return (q1.id<q2.id);
}
int main()
{
    int n;
    scanf("%d",&n);
    len=sqrt(1.0*n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        pos[i]=i/len+1;
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&qu[i].L,&qu[i].R);
        qu[i].id=i;
    }
    sort(qu+1,qu+m+1,comp1);
    int ll=1,rr=0;
    int ans=0;
    for(int i=1;i<=m;i++){
        int L=qu[i].L,R=qu[i].R;
        if(rr<R){
            for(int j=rr+1;j<=R;j++){
                if(!b[a[j]]){
                    ans++;
                }
                b[a[j]]++;
            }
        }
        else{
            for(int j=rr;j>R;j--){
                b[a[j]]--;
                if(!b[a[j]]){
                    ans--;
                }
            }
        }
        if(ll<L){
            for(int j=ll;j<L;j++){
                b[a[j]]--;
                if(!b[a[j]]){
                    ans--;
                }
            }
        }
        else{
            for(int j=ll-1;j>=L;j--){
                if(!b[a[j]]){
                    ans++;
                }
                b[a[j]]++;
            }
        }
        qu[i].ans=ans;
        ll=L; rr=R;
    }
    sort(qu+1,qu+m+1,comp2);
    for(int i=1;i<=m;i++){
        printf("%d\n",qu[i].ans);
    }
    return 0;
}

原创粉丝点击