[bzoj3809]Gty的二逼妹子序列

题目大意

询问序列区间中权值在给定区间内权值种类数。

简单的莫队思想

我们可以莫队算法,然后用线段树来维护,这个思路十分显然。
这样in和out的复杂度均为log n,而query的复杂度为log n。

更高效的算法

同样使用莫队,让我们思考我们需要兹瓷的功能。
1、插入一个数(in)
2、删除一个数(out)
3、询问区间权值种类(query)
上面用线段树来兹瓷这三个操作,由于in和out时间复杂度较大所以总的时间复杂度较大。我们可以考虑减小in和out的复杂度,当然代价是增大query的复杂度。
我们可以分块,于是in和out就变成o(1)的了!相应的query复杂度是根号。

#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100000+10,maxm=1000000+10;
struct dong{
    int l,r,a,b,id;
};
int belong[maxn],a[maxn],ans[maxm],cnt[maxn],num[1000];
dong ask[maxm];
int i,j,k,l,r,t,n,m,c;
bool cmp(dong a,dong b){
    if (belong[a.l]<belong[b.l]) return 1;
    else if (belong[a.l]==belong[b.l]&&a.r<b.r) return 1;
    else return 0;
}
void in(int x){
    cnt[x]++;
    if (cnt[x]==1) num[belong[x]]++;
}
void out(int x){
    cnt[x]--;
    if (cnt[x]==0) num[belong[x]]--;
}
int query(int j,int k){
    int i,t=0;
    int l=belong[j],r=belong[k];
    if (r-l<=1){
        fo(i,j,k)
            if (cnt[i]>0) t++;
        return t;
    }
    fo(i,l+1,r-1) t+=num[i];
    fo(i,j,l*c)
        if (cnt[i]>0) t++;
    fo(i,(r-1)*c+1,k)
        if (cnt[i]>0) t++;
    return t;
}
int read(){
    int x=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}
int main(){
    n=read();m=read();
    c=floor(sqrt(n));
    fo(i,1,n) belong[i]=(i-1)/c+1;
    fo(i,1,n) a[i]=read();
    fo(i,1,m){
        ask[i].l=read();ask[i].r=read();ask[i].a=read();ask[i].b=read();
        ask[i].id=i;
    }
    sort(ask+1,ask+m+1,cmp);
    l=r=1;
    in(a[1]);
    fo(i,1,m){
        while (l<ask[i].l) out(a[l++]);
        while (l>ask[i].l) in(a[--l]);
        while (r<ask[i].r) in(a[++r]);
        while (r>ask[i].r) out(a[r--]);
        ans[ask[i].id]=query(ask[i].a,ask[i].b);
    }
    fo(i,1,m) printf("%d\n",ans[i]);
}

hzwer所说的更加优越算法

具体看2014国家队论文集里,徐寅展的论文。

你可能感兴趣的:([bzoj3809]Gty的二逼妹子序列)