线段树/莫队——BZOJ1878/Luogu1972 [SDOI2009]HH的项链

http://www.lydsy.com/JudgeOnline/problem.php?id=1878
https://www.luogu.org/problem/show?pid=1972
隔了一段时间用新方法重做此题。。。
这个可以看做离线区间查询问题,一段时间以前,我把这题当做是线段树来做
具体思路:把询问按照右端点从小到大排序,然后一个一个插入
意思是说按照项链的真实情况插入
插入某种编号的贝壳时,如果这种编号之前已经插入过了,那么就把之前插入过的贝壳删除
如果插入的节点是某一询问的右端点,处理询问
显然是对的不是吗
线段树也可用树状数组代替
贴一下线段树代码

#include
#include
#include
#include
using namespace std;
struct ppap{
    int x,y,b;
}p[400001];
int n,a[400001],t[400001],ans[400001],la[1000001]={0};
inline bool cmp(ppap x,ppap y){
    if(x.yreturn true;
    if(x.y==y.y&&x.xreturn true;
    return false;
}
inline void xg(int l,int r,int i,int p,int nod){
    if(l==i&&r==i)t[nod]=p;
    else{
        int m=(l+r)/2;
        if(i>m)xg(m+1,r,i,p,nod*2+1);
        else xg(l,m,i,p,nod*2);
        t[nod]=t[nod*2]+t[nod*2+1];
    }
}
inline int s(int l,int r,int i,int j,int nod){
    if(l==i&&r==j)return t[nod];
    int m=(l+r)/2;
    if(j<=m)return s(l,m,i,j,nod*2);
    if(i>=m+1)return s(m+1,r,i,j,nod*2+1);
    int le=s(l,m,i,m,nod*2),re=s(m+1,r,m+1,j,nod*2+1);
    return le+re;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    int m;scanf("%d",&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&p[i].x,&p[i].y);
        p[i].b=i;
    }
    sort(p+1,p+m+1,cmp);
    int pp=1;
    for(int i=1;i<=n;i++){
        xg(1,n,i,1,1);
        if(la[a[i]]!=0)xg(1,n,la[a[i]],0,1);
        la[a[i]]=i;
        while(p[pp].y==i){
            ans[p[pp].b]=s(1,n,p[pp].x,p[pp].y,1);
            pp++;
        }
    }
    for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
    return 0;
}

一段时间以后发现这题可以轻松用莫队水过。。。
直接分块暴力跑得比常数大的线段树快呢!!!

#include
using namespace std;
struct ppap{
    int x,y,k,b;
}a[100001];
int s[1000001];
int n,m,p[100001],ans[100001];
inline bool cmp(ppap a,ppap b){return a.k==b.k?a.yinline void work(){
    int l=1,r=0,now=0;
    for(int i=1;i<=m;i++){
        while(lif(s[p[l]]==0)now--;l++;}
        while(r>a[i].y){s[p[r]]--;if(s[p[r]]==0)now--;r--;}
        while(l>a[i].x){s[p[l-1]]++;if(s[p[l-1]]==1)now++;l--;}
        while(r1]]++;if(s[p[r+1]]==1)now++;r++;}     
        ans[a[i].b]=now;
    }
}
int main()
{
    scanf("%d",&n);
    int t=sqrt(n);
    for(int i=1;i<=n;i++)scanf("%d",&p[i]);
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&a[i].x,&a[i].y);
        a[i].b=i;
        a[i].k=(a[i].x-1)/t+1;
    }
    sort(a+1,a+m+1,cmp);
    work();
    for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
    return 0;
}

你可能感兴趣的:(分块/莫队,线段树/树状数组)