• 莫队学习总结


    小Z的袜子 (HYSBZ-2038)

    作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。 你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子。当然,小Z希望这个概率尽量高,所以他可能会询问多个(L,R)以方便自己选择。

    思路:模板题

    某个区间([l,r])的答案:

    [{sum_{i为出现的颜色} sum[i]cdot (sum[i]-1)/2}over {C_{r-l+1}^2} ]

    1. 对询问排序
      1. ([l,r]),以(l)所在块的编号为第一关键字,r为第二关键字从小到大排序。
    2. 暴力维护答案的分子部分即可
    3. 可以发现答案分子分母同时将2约掉,分子展开后变成(sum[i]cdot sum[i] - sum[i])可以发现对于所有的 i ,(sum[i])的和将变成(r-l+1),所以我们只需要维护所有(sum[i])的平方和即可
    #include <iostream>
    #include <algorithm>
    #include <math.h>
    #include <cstdio>
    using namespace std;
    const int N = 50010;
    typedef long long ll;
    int a[N],be[N],n,m;
    ll res = 0,sum[N];
    struct node{
        int l,r,id;
        ll A,B;
    }q[N];
    //如果在同一块,则按照右端点排序,否则按照左端点
    bool cmp1(node a,node b){
        return be[a.l] == be[b.l] ? a.r < b.r : a.l < b.l;
    }
    bool cmp(node a,node b){
        return a.id < b.id;
    }
    ll gcd(ll a,ll b){return b == 0 ? a : gcd(b,a%b);}
    ll S(ll x){return x * x;}
    //先减去上一次的影响,修改后再重新加新的影响
    void move(int pos,int add){res -= S(sum[a[pos]]);sum[a[pos]] += add;res += S(sum[a[pos]]);}
    int main(){
        scanf("%d%d",&n,&m);
        int base = sqrt(n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);q[i].id = i;
            be[i] = (i-1) / base + 1;//be[i]即i在第几块
        }
        for(int i=1;i<=m;i++)scanf("%d%d",&q[i].l,&q[i].r);
        sort(q+1,q+1+m,cmp1);
        int l = 1,r = 0;
        res = 0;//res为当点询问区间内出现的所有颜色的个数平方和
        for(int i=1;i<=m;i++){
            //暴力调整区间,维护res
            while(l < q[i].l)move(l++,-1);
            while(l > q[i].l)move(--l,1);
            while(r < q[i].r)move(++r,1);
            while(r > q[i].r)move(r--,-1);
            if(l == r){
                q[i].A = 0;q[i].B = 1;continue;
            }
            q[i].A  = res - (r - l + 1);//计算答案分子部分
            q[i].B = 1ll * (r - l + 1) * (r - l);//分母部分
            ll g = gcd(q[i].A,q[i].B);//约分
            q[i].A /= g;
            q[i].B /= g;
        }
        sort(q+1,q+1+m,cmp);
        for(int i=1;i<=m;i++){
            printf("%lld/%lld
    ",q[i].A,q[i].B);
        }
        return 0;
    }
    

    普通莫队优化

    可以发现当第一块内的询问处理完之后,r的位置应该特别靠后,但是当移动到下一个块之后,r可能会往前移动很多,比如如下询问

    //第一个块
    1 50
    2 100
    //第二个块
    12 13
    14 100
    

    在完成[2,100]的询问后,r从100-> 13 然后又从13 -> 100。这样显然不如100->100, 100 -> 13。

    如何优化?

    相邻两块之间r的排序规则相反即可

    即奇数块按照升序,偶数快按照降序

    Result Memory Time
    Accepted 3456 kb 1840 ms
    Accepted 3456 kb 1392 ms

    下面的是优化过的。

    bool cmp1(node a,node b){
        return be[a.l] == be[b.l] ? 
            (be[a.l]&1 ? a.r < b.r : a.r > b.r)
            : a.l < b.l;
    }
    

    带修改的莫队

    考虑普通莫队加入修改修做,如果修改操作可以(O(1))的应用以及撤销(同时也要维护当前区间的答案),那么可以在(O(n^{5over 3}))的复杂度内求出所有询问的答案。

    实现: 离线后排序,顺序遍历询问,先将时间转移到当前询问的时间,然后再像普通莫队一样转移区间。

    排序方法: 设定块的长度为(S_1,S_2),按照((lfloor{lover S_1} floor lfloor{rover S_2} floor,t))的三元组小到大排序,其中 (t) 表示这个询问的时刻之前经历过了几次修改操作

    复杂度分析:考虑询问序列中的每个小块,小块内每个询问的一二关键字相同。在这个小块内,时间 (t) 最多变化 (m) ,对于每个询问,(l,r) 最多变化 (S_1,S_2), 一共右(n^2over {S_1,S_2}) 个这样的块,相邻块之间转移复杂度是(O(n)), 总复杂度就是

    (O(mS_1+mS_2+{n^2mover S_1S_2}+{n^3over S_1S_2}))

    (n,m)同阶时,取(S_1 = S_2 = n^{2over 3}) 时可达到最优复杂度(O(n^{5over 3}))

    int l = 0, r = 0, t = 0, nowAns = 0;
    
    inline void move(int pos, int sign) {
        // update nowAns
    }
    
    inline void moveTime(int t, int sign) {
        // apply or revoke modification
        // update nowAns
    }
    
    void solve() {
        BLOCK_SIZE = int(ceil(pow(n, 2.0 / 3)));
        sort(querys, querys + m);
        for (int i = 0; i < q1; ++i) {
            const query q = querys[i];
            while (t < q.t) moveTime(t++, 1);
            while (t > q.t) moveTime(--t, -1);
            while (l < q.l) move(l++, -1);
            while (l > q.l) move(--l, 1);
            while (r < q.r) move(r++, 1);
            while (r > q.r) move(--r, -1);
            ans[q.id] = nowAns;
        }
    }
    
  • 相关阅读:
    sed 拓展 awk 拓展
    9.6/9.7 awk
    9.4/9.5 sed
    正则介绍_grep上& grep中 & grep下
    shell特殊符号cut命令& sort_wc_uniq命令 & tee_tr_split命令 & shell特殊符号
    管道符和作业控制 & shell变量& 环境变量配置文件
    shell介绍 & 命令历史 & 命令补全和别名& 通配符& 输入输出重定向
    yum更换国内源 & yum下载rpm包 & 源码包安装
    网络编程入门(下)
    网络编程入门(上)
  • 原文地址:https://www.cnblogs.com/1625--H/p/11329586.html
Copyright © 2020-2023  润新知