zoukankan      html  css  js  c++  java
  • 莫队算法学习笔记

    莫队算法

    一句话算法:莫队是一种基于分块和询问排序思想的序列处理算法,因此大部分时间,我们需要离线询问,再对询问按某些优先级排序处理。

    例题

    详情见小Z的袜子,推导过程与莫队无关故略去,这里直接给出结论:
    对于一个询问([l, r])(ans = frac{sumlimits_{i = 1}^{C}cnt[i] * (cnt[i] - 1)}{(r - l + 1)(r - l)}),其中(C)为颜色的值域

    考虑普通暴力:
    维护两个指针(l,r),表示当前处理的区间,每次挪动两指针来靠近询问并修改当前答案
    挪动的时候减掉和加上当前点的贡献的操作相信大家都会,比如对于这个例题,我要向左向右挪动一位,那么对应的程序是这样的

    void modify(int k, int d) {
    	ans -= cnt[col[k]] * (cnt[col[k]] - 1);
    	cnt[col[k]] += d;
    	ans += cnt[col[k]] * (cnt[col[k]] - 1);
    }
    

    其中(d)(1/-1),表示此次挪动是添加还是删除

    显然这样我们还是会被毒瘤出题人卡到(O(nm)),比如如果每次询问都需要把(l,r)指针横跨整个序列地挪动
    考虑对于询问,我们要怎样处理,才能够减小总的时间复杂度,而不是寄希望于数据湿度上呢
    于是莫涛大神提出了一个算法,可以将处理这类问题的复杂度优化到(O(nsqrt n))
    莫队算法就此产生

    算法流程

    首先我们离线所有询问,并把序列按某种方式分块。
    对于我们得到的询问,按第一关键字为左端点所属块的序号,第二关键字为右端点的位置排序。
    对于我们得到的询问,再按暴力的方式去处理它

    这样做就有用吗?对于时间复杂度的优化程度如何?
    下面给出证明。


    时间复杂度证明

    首先对于一次挪动,答案的更新是(O(1))
    对于(l)(r)指针的挪动分别讨论:
    设块大小为(base)

    • 对于(l)指针的挪动,当它不需要跨块的时候,它的单次最坏复杂度是(O(base))的,而需要跨块的时候单次最坏复杂度是(O(2 * base))的,常数(2)可以忽略不计,于是对于总共(m)次的挪动,(l)的挪动是(O(m*base))级别的。
    • 对于(r)指针的挪动,由于(r)指针在对应的(l)指针在同一块内是有序的,故对于每一块内的(l),右端点的挪动最坏情况是(O(n)),而左端点最劣是从头到尾一共(frac{n}{base})块都挪一遍,所以(r)指针的挪动是(O(n * frac{n}{base}))
      左右指针的挪动互不干涉,故总的时间复杂度是两者相加,即(O(m * base + n * frac{n}{base}))
      (m, n)同阶时,(base)(sqrt n)有最优时间复杂度(O(2 * n * sqrt n))

    本题代码:

    #include<bits/stdc++.h>
    #define N (100000 + 10)
    using namespace std;
    typedef long long ll;
    int col[N], n, m, pos[N];
    ll cnt[N], ans;
    ll ans1[N], ans2[N];
    inline int read() {
    	int cnt = 0, f = 1; char c = getchar();
    	while(!isdigit(c)) {if (c == '-') f = -f; c = getchar();}
    	while(isdigit(c)) {cnt = (cnt << 3) + (cnt << 1) + (c ^ 48); c = getchar();}
    	return cnt * f;
    }
    struct Q {
    	int l, r, id;
    }q[N];
    bool cmp (Q a, Q b) {
    	return (pos[a.l] == pos[b.l]) ? a.r < b.r : pos[a.l] < pos[b.l];
    }
    ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    void modify(int k, int d) {
    	ans -= cnt[col[k]] * (cnt[col[k]] - 1);
    	cnt[col[k]] += d;
    	ans += cnt[col[k]] * (cnt[col[k]] - 1);
    }
    int main() {
    	n = read(), m = read();
    	for (register int i = 1; i <= n; ++i) col[i] = read();
    	for (register int i = 1; i <= m; ++i) q[i].l = read(), q[i].r = read(), q[i].id = i;
    	int base = sqrt(n);
    	for (register int i = 1; i <= n; ++i) pos[i] = i / base;
    	sort (q + 1, q + m + 1, cmp);
    	int L = 1, R = 1;
    	++cnt[col[1]];
    	for (register int i = 1; i <= m; ++i) {
    		if (q[i].l == q[i].r) {ans1[q[i].id] = 0, ans2[q[i].id] = 1; continue;}
    		while (R < q[i].r) modify(++R, 1);
    		while (R > q[i].r) modify(R--, -1);
    		while (L < q[i].l) modify(L++, -1);
    		while (L > q[i].l) modify(--L, 1);
    		ll len = q[i].r - q[i].l + 1;
    		ll tot = len * (len - 1);
    		ll g = gcd(ans, tot);
    		ans1[q[i].id] = ans / g, ans2[q[i].id] = tot / g;
    	}
    	for (register int i = 1; i <= m; ++i) printf("%lld/%lld
    ", ans1[i], ans2[i]);
    	return 0;
    }
    
  • 相关阅读:
    关于Maya Viewport 2.0 API 开发的介绍视频
    春节大假
    Some tips about the life cycle of Maya thread pool
    Can I compile and run Dx11Shader for Maya 2015 on my side?
    How to get current deformed vertex positions in MoBu?
    想加入全球首届的 欧特克云加速计划吗?
    三本毕业(非科班),四次阿里巴巴面试,终拿 offer(大厂面经)
    mac、window版编辑器 webstorm 2016... 永久破解方法。
    node 搭载本地代理,处理web本地开发跨域问题
    js 一维数组,转成嵌套数组
  • 原文地址:https://www.cnblogs.com/kma093/p/12036305.html
Copyright © 2011-2022 走看看