[BZOJ3809]Gty的二逼妹子序列(莫队+分块)

题目描述

传送门

题解

莫队+树状数组的思路很显然,但是时间 O(mnlog2n)
莫队+权值分块的做法比较优越。将权值分块之后修改可以做到 O(1) 查询 O(n)

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;

const int max_n=1e5+5;
const int max_m=1e6+5;
const int max_sqrt=350;

int n,m,t,num,ans;
int a[max_n],val[max_n],sum[max_sqrt];
struct hp{int l,r,a,b,id,final;}q[max_m];

inline int cmp(hp a,hp b){
    int num1,num2;
    if (a.l%t==0) num1=a.l/t; else num1=a.l/t+1;
    if (b.l%t==0) num2=b.l/t; else num2=b.l/t+1;
    return num1<num2||(num1==num2&&a.r<b.r);
}
inline int ask(int l,int r){
    int ans=0,k,lrange,rrange,numl,numr;
    if (l%t==0) numl=l/t;
    else numl=l/t+1;
    if (r%t==0) numr=r/t;
    else numr=r/t+1;

    if (numl==numr){
        for (int i=l;i<=r;++i)
          if (val[i]) ans++;
        return ans;
    }

    lrange=numl+1;
    k=numl*t;
    for (int i=l;i<=k;++i)
      if (val[i]) ans++;
    k=(numr-1)*t+1;
    for (int i=k;i<=r;++i)
      if (val[i]) ans++;
    rrange=numr-1;
    for (int i=lrange;i<=rrange;++i)
      ans+=sum[i];
    return ans;
}
int main(){
    scanf("%d%d",&n,&m);
    t=(int)sqrt(n);
    for (int i=1;i<=n;++i) scanf("%d",&a[i]);
    for (int i=1;i<=m;++i) scanf("%d%d%d%d",&q[i].l,&q[i].r,&q[i].a,&q[i].b),q[i].id=i;
    sort(q+1,q+m+1,cmp);

    for (int i=q[1].l;i<=q[1].r;++i){
        if (a[i]%t==0) num=a[i]/t; else num=a[i]/t+1;
        ++val[a[i]];
        if (val[a[i]]==1) ++sum[num];
    }
    ans=ask(q[1].a,q[1].b);
    q[q[1].id].final=ans;

    for (int i=2;i<=m;++i){
        if (q[i-1].l<q[i].l)
          for (int j=q[i-1].l;j<q[i].l;++j){
            if (a[j]%t==0) num=a[j]/t; else num=a[j]/t+1;
            --val[a[j]];
            if (!val[a[j]]) --sum[num];
          }
        if (q[i-1].l>q[i].l)
          for (int j=q[i-1].l-1;j>=q[i].l;--j){
            if (a[j]%t==0) num=a[j]/t; else num=a[j]/t+1;
            ++val[a[j]];
            if (val[a[j]]==1) ++sum[num];
          }
        if (q[i-1].r<q[i].r)
          for (int j=q[i-1].r+1;j<=q[i].r;++j){
            if (a[j]%t==0) num=a[j]/t; else num=a[j]/t+1;
            ++val[a[j]];
            if (val[a[j]]==1) ++sum[num];
          }
        if (q[i-1].r>q[i].r)
          for (int j=q[i-1].r;j>q[i].r;--j){
            if (a[j]%t==0) num=a[j]/t; else num=a[j]/t+1;
            --val[a[j]];
            if (!val[a[j]]) --sum[num];
          }
        ans=ask(q[i].a,q[i].b);
        q[q[i].id].final=ans;
    }
    for (int i=1;i<=m;++i)
      printf("%d\n",q[i].final);
}

你可能感兴趣的:(分块,bzoj,莫队)