• 回滚莫队学习笔记


    \(\text{Part1}\)

    如果是只增,那么将询问按左端点所在块为第一关键字升序,右端点为第二关键字升序排序
    如果询问在一个块内,暴力扫
    不然对于左端点在同一个块的所有询问,先将莫队区间左端点移到块右端点 \(+1\),右端点移到块右端点,这是一个空区间
    然后右端点递增,保证了只增,左端点向左也是只增,每个询问增完后要将左端点回滚到增之前的位置,为了以后只增
    \(O(n\sqrt m+m\sqrt n)\)

    例如

    \(\text{「JOISC 2014 Day1」历史研究}\)

    \(\text{Code}\)

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #define RE register
    #define IN inline
    using namespace std;
    typedef long long LL;
    
    const int N = 1e5 + 5;
    int n, m, a[N], b[N], c[N], R[N], buc[N], tbuc[N], pos[N];
    LL ans[N];
    struct node{int l, r, id;}Q[N];
    IN bool cmp(node a, node b){return pos[a.l] < pos[b.l] ? 1 : (pos[a.l] == pos[b.l] ? a.r < b.r : 0);}
    
    IN void Init()
    {
    	scanf("%d%d", &n, &m);
    	for(RE int i = 1; i <= n; i++) scanf("%d", &a[i]), b[i] = a[i];
    	sort(b + 1, b + n + 1); int len = unique(b + 1, b + n + 1) - b - 1;
    	for(RE int i = 1; i <= n; i++) c[i] = lower_bound(b + 1, b + len + 1, a[i]) - b;
    	int bl = sqrt(n);
    	for(RE int i = 1; i <= m; i++)
    		scanf("%d%d", &Q[i].l, &Q[i].r), pos[Q[i].l] = Q[i].l / bl, pos[Q[i].r] = Q[i].r / bl, Q[i].id = i;
    	sort(Q + 1, Q + m + 1, cmp), R[0] = bl - 1, R[n / bl] = n;
    	for(RE int i = 1; i < n / bl; i++) R[i] = R[i - 1] + bl;
    }
    IN void Add(int x, LL &s){++buc[c[x]], s = max(s, (LL)a[x] * buc[c[x]]);}
    IN void Del(int x){--buc[c[x]];}
    
    int main()
    {
    	Init();
    	int l = 1, r = 0, lst = -1, tl; LL Ans = 0, tmp;
    	for(RE int i = 1; i <= m; i++)
    	{
    		if (pos[Q[i].l] == pos[Q[i].r])
    		{
    			for(RE int j = Q[i].l; j <= Q[i].r; j++)
    				++tbuc[c[j]], ans[Q[i].id] = max(ans[Q[i].id], (LL)a[j] * tbuc[c[j]]);
    			for(RE int j = Q[i].l; j <= Q[i].r; j++) --tbuc[c[j]];
    			continue;
    		}
    		if (pos[Q[i].l] != lst)
    		{
    			while (r > R[pos[Q[i].l]]) Del(r--);
    			while (l < R[pos[Q[i].l]] + 1) Del(l++);
    			Ans = 0, lst = pos[Q[i].l];
    		}
    		while (r < Q[i].r) Add(++r, Ans);
    		tl = l, tmp = Ans;
    		while (tl > Q[i].l) Add(--tl, tmp);
    		ans[Q[i].id] = tmp;
    		while (tl < l) Del(tl++);
    	}
    	for(RE int i = 1; i <= m; i++) printf("%lld\n", ans[i]);
    }
    

    \(\text{Part2}\)

    只删的话,对比只增,左端点同块时,需要莫队区间 \(r\) 的移动是递减的
    于是将询问按左端点所在块为第一关键字升序,右端点为第二关键字降序排序
    如果询问在一个块内,暴力扫
    不然对于左端点在同一个块的所有询问,先将莫队区间左端点移到块左端点,右端点移动到 \(n\),这是一个大区间
    然后右端点递减,保证了只删,左端点向右也是只删,每个询问删完后要将左端点回滚到删之前的位置,为了以后只删
    例如区间 \(mex\)

  • 相关阅读:
    内聚和耦合的举例
    OneZero第四周第五次站立会议(2016.4.15)
    OneZero第四周第四次站立会议(2016.4.14)
    OneZero团队Beta发布剧透
    PSP(4.6——4.12)以及周记录
    关于“内聚和耦合”
    CSV 注入实战
    BurpSuite 一些小技巧
    博客园URL跳转钓鱼
    【Demo 0005】Android 资源
  • 原文地址:https://www.cnblogs.com/leiyuanze/p/15881754.html
Copyright © 2020-2023  润新知