zoukankan      html  css  js  c++  java
  • 题解 【DQUERY Dquery】

    题目描述:

    给出一串长度为 \(n\) 的序列,有 \(q\) 个询问,每次询问给数对 \((i,j)\),求区间 \([i,j]\) 中有多少个不同的数字


    这题我是用莫队过的;
    众所周知,莫队是一个暴力毒瘤玄学很方便的算法(不套其他的数据结构),可以乱搞很多的区间问题。

    p.s.我是通过这篇博客来自学的,如果有理解不对的地方,还望 dalao 指正


    进入正题:
    莫队算法将询问排序后,通过移动左右端点来更改答案,从而保证它优秀的复杂度。

    在排序询问时,我们要用到分块的思想:
    我们将整个区间分成 \(\sqrt{n}\) 个块。

    在排序的时候,如果左端点在同一个块,就将右端点按从大到小排序;否则,按左端点的块从大到小排序。

    这样保证了每个端点一次最多移动 \(\sqrt{n}\) 格,从而保证了复杂度。

    friend bool operator < (qus a,qus b){
        return k[a.l]==k[b.l]?a.r<b.r:k[a.l]<k[b.l];
    }
    

    然后在排序的时候可以将普通的排序,换成按块的奇偶分类排序,可以让你的代码玄学咕咕咕的加快。

    friend bool operator < (qus a,qus b){
        return k[a.l]^k[b.l]?k[a.l]<k[b.l]:(k[a.l]&1?a.r<b.r:a.r>b.r);
    }
    

    之后便是 AddDel 函数,用来在移动的同时更新答案。

    用 num 来记录目前的区间 \([l,r]\) 的数字种类,\(cnt_x\) 来记录 x 出现的次数;

    在加入第 pos 个数时,如果该数在这个区间第一次出现,则将总数加一:

    void Add(int pos){
        if(!cnt[a[pos]]) ++num;
        ++cnt[a[pos]];
    }
    

    在删除第 pos 个数时,如果删除后这个数在区间内不在出现,则将总数减一:

    void Del(int pos){
        --cnt[a[pos]];
        if(!cnt[a[pos]]) --num;
    }
    

    然后是转移左右端点。

    如果当前的左端点,小于答案的左端点,则将该点删除,并将左端点向右移;
    如果当前的左端点,大于答案的左端点,则将左端点左移,并且将该点加入;
    如果当前的右端点,大于答案的右端点,则将该点删除,并将右端点向左移;
    如果当前的右端点,小于答案的右端点,这将右端点右移,并且将该点加入。
    (这不是绕口令)

        while(L<q[i].l) Del(L++);
        while(L>q[i].l) Add(--L);
        while(R<q[i].r) Add(++R);
        while(R>q[i].r) Del(R--);
    

    最后在统计答案的时候,按照原先输入的顺序输出就好了。


    代码如下(码风有点丑):

    #include<bits/stdc++.h>
    #define rint register int
    using namespace std;
    inline int read(){
        int s=0,f=1; char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=0;c=getchar();}
        while(c>='0'&&c<='9') s=(s<<1)+(s<<3)+(c^48),c=getchar();
        return f?s:-s;
    }
    int n,a[30010],cnt[1000010],num=0,Q;
    int k[1010],ks,ans[200010];
    struct qus{
        int l,r,id;
        friend bool operator < (qus a,qus b){
            return k[a.l]^k[b.l]?k[a.l]<k[b.l]:(k[a.l]&1?a.r<b.r:a.r>b.r);
        }
    }q[200010];
    void Del(int pos){
        --cnt[a[pos]];
        if(!cnt[a[pos]]) --num;
    }
    void Add(int pos){
        if(!cnt[a[pos]]) ++num;
        ++cnt[a[pos]];
    }
    int main(){
        n=read(); ks=sqrt(n);
        for(rint i=1;i<=n;++i) a[i]=read();
        for(rint i=1;i<=n;++i) k[i]=(i-1)/ks+1;
        Q=read();
        for(rint i=1;i<=Q;++i){
        	q[i].l=read();
            q[i].r=read();
            q[i].id=i;
        }
        sort(q+1,q+1+Q);
        int L=1,R=0;
        for(rint i=1;i<=Q;++i){
            while(L<q[i].l) Del(L++);
            while(L>q[i].l) Add(--L);
            while(R<q[i].r) Add(++R);
            while(R>q[i].r) Del(R--);
            ans[q[i].id]=num;
        }
        for(rint i=1;i<=Q;++i)
        	printf("%d\n",ans[i]);
        return 0;
    }
    

    咕咕咕~

  • 相关阅读:
    JAVA Aes加密解密
    15个实用的jQuery代码片
    mybatis-generator-config工具的使用
    Oracle中Merge into用法总结
    Highcharts 在低版本 IE 上使用注意事项及个人总结
    梦想是什么
    博客美化基本后台设置与样式设置
    IntelliJ Idea 常用快捷键列表
    磁盘
    [半转]1px边框 移动端
  • 原文地址:https://www.cnblogs.com/LCGUO/p/12426171.html
Copyright © 2011-2022 走看看