Group (莫队算法)

原题:Group

题意:

一个数组,给多个区间,每个位置有个不会重复的编号,编号连续的数可以组队,k个数的队价值为k*k,每个询问给一个区间,求区间的最大价值需要多少个组

解析:

2*2+1*1肯定是3*3大,所以相当于问在排序后有多少片连续的数

对于这种分析数出现不出现,出现几次的问题,且多个询问可以离线的情况,可以用莫队来做


首先,离线处理后,进行这个算法最重要的询问排序

对于询问(l,r),我们对左界分块,同分块,k=sqrt(n)最快,而对于l属于相同块的询问,我们按照r进行升序排列

这样排序的好处是

  1. 如果当前询问与下一个询问同块,左指针最多移动sqrt(n)的距离
  2. 当前块内有多少询问,当前块内的右指针只会向右走,所以这个块的右指针至多走n次,而左指针至多走q*sqrt(n)次

  3. 一个块内的复杂度最多为 n+q*sqrt(n).

    扫描二维码关注公众号,回复: 2479140 查看本文章

在排序好后,我们用两个指针分别指向当前询问的左界和右界,在跳到下一个询问的时候只需要移动两个指针就好(类似尺取法),对加进来的数,have[]++,删去的数- -,就可以维护当前区间的数了


而加进来的数可以连接当前的两条(2,4,5加进3)就可以使条数- -,连一边则不变(2,3加进4),一边都没有就++。注意一下点1,因为没有点0

代码:

//写的有点冗长了
D read(){ D ans=0; char last=' ',ch=getchar();
while(ch<'0' || ch>'9')last=ch,ch=getchar();
while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
if(last=='-')ans=-ans; return ans;
}

int a[100009];
int k;
int idx(int a){
    return (a-1)/k+1;
}

int have[100009];

struct node{
    int l,r,_id;
    bool operator <(const node a)const{
        if(idx(l)!=idx(a.l)){
            return l<a.l;
        }
        return r<a.r;
    }
}e[100009];
int ans[100009];

int main(){
    int t=read();while(t--){
        mmm(have,0);
        int n=read(),q=read();
        k=int(sqrt(n));
        for(int i=1;i<=n;i++)a[i]=read();
        for(int i=1;i<=q;i++)e[i]._id=i,e[i].l=read(),e[i].r=read();
        sort(e+1,e+1+q);
        int l=0,r=0;
        int nowans=0;
        for(int i=1;i<=q;i++){
            while(r<e[i].r){
                r++;have[a[r]]++;
                if(a[r]==0);
                else if(a[r]==1){
                    if(have[2]<=0)nowans++;
                }
                else if(have[a[r]-1]&&have[a[r]+1])nowans--;
                else if(have[a[r]-1]<=0&&have[a[r]+1]<=0)nowans++;

            }
            while(l<e[i].l){
                have[a[l]]--;
                if(a[l]==0);
                else if(a[l]==1){
                    if(have[2]<=0)nowans--;
                }
                else if(have[a[l]-1]&&have[a[l]+1])nowans++;
                else if(have[a[l]-1]<=0&&have[a[l]+1]<=0)nowans--;


                l++;
            }
            while(r>e[i].r){
                have[a[r]]--;
                if(a[r]==0);
                else if(a[r]==1){
                    if(have[2]<=0)nowans--;
                }
                else if(have[a[r]-1]&&have[a[r]+1])nowans++;
                else if(have[a[r]-1]<=0&&have[a[r]+1]<=0)nowans--;

                r--;
            }
            while(l>e[i].l){
                l--;have[a[l]]++;
                if(a[l]==0);
                else if(a[l]==1){
                    if(have[2]<=0)nowans++;
                }
                else if(have[a[l]-1]&&have[a[l]+1])nowans--;
                else if(have[a[l]-1]<=0&&have[a[l]+1]<=0)nowans++;

            }
            ans[e[i]._id]=nowans;
        }
        for(int i=1;i<=q;i++){
            printf("%d\n",ans[i]);
        }
    }
}

重新整理了一下莫队,再发一题

原题:HDU-3333 Turing Tree

题意:

每次询问求区间l,r内所有不同的数的总和

当然了,这次除了莫队以外还需要离散化才能过。

对于每个A[i],先map得到这个数的标号,然后对于原数组的每个位置都放那个位置上数的标号,这样预处理后就可以用数组代替map存出现次数了

代码:


int a[30009];
int k;
int qid(int x){return (x-1)/k+1;}
struct node{
    int l,r,idx;
    bool operator < (const node a){
        if(qid(l)==qid(a.l))return r<a.r;
        return l<a.l;
    }
}q[100009];

map<int,int>tmp;
int id[30009];//id[3]==4:位置3放的是标号为4的数
int have[30009];
D sum;
D ans[100009];
void del(int p){int x=id[p];have[x]--;if(!have[x])sum-=(D)a[p];}
void add(int p){int x=id[p];if(!have[x])sum+=(D)a[p];have[x]++;}
int main(){
    int t=read();
    while(t--){
        mmm(have,0);sum=0;tmp.clear();
        int n=read();k=(int)sqrt(n);
        int pos=0;
        for1(i,1,n){
            a[i]=read();
            if(tmp.find(a[i])==tmp.end())id[i]=++pos,tmp[a[i]]=pos;
            else id[i]=tmp[a[i]];
        }
        int que=read();for1(i,1,que)q[i].l=read(),q[i].r=read(),q[i].idx=i;
        sort(q+1,q+1+que);
        int l=0,r=0;
        for1(i,1,que){
            while(q[i].r>r)add(++r);
            while(q[i].l<l)add(--l);
            while(q[i].r<r)del(r--);
            while(q[i].l>l)del(l++);
            ans[q[i].idx]=sum;
        }
        for1(i,1,que)printf("%lld\n",ans[i]);
    }
}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/81103419