zoukankan      html  css  js  c++  java
  • [洛谷P4117][CF896E][Ynoi2018]五彩斑斓的世界(分块)

    Description

    • 给定一个长度为\(n\)的序列\(a\),有\(m\)次操作:
    • (1).把区间\([l,r]\)中大于\(v\)的数减去\(v\)
    • (2).查询区间\([l,r]\)\(v\)的出现次数
    • 所有输入均在\([1,100000]\)范围内

    Solution

    • 这种神仙题当然是分块
    • 如果只考虑整块操作,那么处于同一块且相同的数,每次要么一起减\(v\)要么一起不减
    • 那么考虑将同一块中相同的数放一起处理
    • 维护并查集
    • \(h[x][y]\)为块\(x\)中,数值\(y\)的第一次出现位置,没有出现则为\(0\)
    • \(val[find(x)]\)\(a[x]\)所在并查集的数值(\(find(x)\)\(x\)所在并查集的根,也就是\(h[\)\(x\)所在块\(][a[x]]\)
    • \(cnt[find(x)]\)表示这一块与\(a[x]\)相同大小的数有几个,即所在并查集的大小
    • 对于两边不完整的块的单点修改,修改后要重新计算整块的信息
    • 对于整块修改,记\(mx[x]\)为块\(x\)内的最大值
    • 发现\(mx[x]\)的总减少量最多\(100000\)
    • 那么考虑用\(O(v)\)的时间复杂度完成一次整块修改
    • 如果\(mx[x]<=2v\),那么枚举需要减\(v\)的数值,最多\(v\)
    • 如果\(mx[x]>2v\)呢?
    • 又发现不论如何,小于等于\(v\)的数值总是不超过\(v\)
    • 因此可以将小于等于\(v\)的数加上\(v\),然后整块减\(v\)
    • 需要打标记,记\(tag[x]\)为块\(x\)整块减去的值
    • 那么上面的分类讨论改为\(mx[x]-tag[x]\)\(2v\)比较大小
    • 整块修改时也要修改\(h\)\(cnt\)\(val\)
    • 发现\(mx[x]\)也需要修改
    • 如果\(mx[x]>2v\),执行“将小于等于\(v\)的数加上\(v\)”后,\(mx[x]\)不变
    • 否则,\(mx[x]\)必定不增大,那么写一句\(while(!h[x][mx[x]]) mx[x]--;\)就好了
    • 查询整块利用\(cnt[v][tag[x]+v]\)即可,注意\(tag[x]+v\)可能会越界

    Code

    #pragma GCC optimize(2)
    #include <bits/stdc++.h>
    
    using namespace std;
    
    template <class t>
    inline void read(t & res)
    {
        char ch;
        while (ch = getchar(), !isdigit(ch));
        res = ch ^ 48;
        while (ch = getchar(), isdigit(ch))
        res = res * 10 + (ch ^ 48);
    } 
    
    const int e = 1e5 + 5, o = 320;
    int h[o][e];
    int bl[o], br[o], bel[e], f[e], cnt[e], n, m, val[e], mx[o], tag[o], a[e], s;
    
    inline int find(int x)
    {
        return f[x] == x ? x : f[x] = find(f[x]);
    }
    
    inline void merge(int x, int y)
    {
        cnt[x] += cnt[y];
        f[y] = x;
    }
    
    inline void build(int x)
    {
        int l = bl[x], r = br[x], i;
        mx[x] = 0;
        for (i = l; i <= r; i++) a[i] = val[find(i)];
        for (i = l; i <= r; i++)
        {
            f[i] = i;
            cnt[i] = 1;
            h[x][a[i]] = 0;
            mx[x] = max(mx[x], a[i]);	
        }
        for (i = l; i <= r; i++)
        {
            int &t = h[x][a[i]];
            if (!t) 
            {
                t = i;
                val[i] = a[i];
            }
            else merge(t, i);
        }
    }
    
    inline void upt(int l, int r, int v)
    {
        int i, x = bel[l], frm = bl[x], to = br[x];
        for (i = frm; i <= to; i++) 
        {
            a[i] = val[find(i)];
            h[x][a[i]] = 0;
        }
        for (i = l; i <= r; i++)
        if (a[i] - tag[x] > v) a[i] -= v;
        for (i = frm; i <= to; i++)
        {
            val[i] = a[i];
            f[i] = i;
        }
        build(x);
    }
    
    inline void change(int x, int v)
    {
        if (mx[x] - tag[x] <= v) return;
        int i, &t = tag[x], lim = min(mx[x], t + v);
        if (mx[x] - t >= 2 * v)
        {
            for (i = t + 1; i <= lim; i++)
            {
                int &y = h[x][i];
                if (!y) continue;
                int &u = h[x][i + v];
                if (!u) 
                {
                    u = y;
                    val[y] = i + v;
                    y = 0;
                }
                else 
                {
                    merge(u, y);
                    y = 0;
                }
            }
            t += v;
        }
        else
        {
            for (i = t + v + 1; i <= mx[x]; i++)
            {
                int &y = h[x][i];
                if (!y) continue;
                int &u = h[x][i - v];
                if (!u)
                {
                    u = y;
                    val[y] = i - v;
                    y = 0;
                }
                else
                {
                    merge(u, y);
                    y = 0;
                }
            }
            while (!h[x][mx[x]]) mx[x]--;
        }
    }
    
    inline void update(int l, int r, int v)
    {
        int x = bel[l], y = bel[r];
        if (x == y)
        {
            upt(l, r, v);
            return;
        }
        upt(l, br[x], v);
        upt(bl[y], r, v);
        int i;
        for (i = x + 1; i < y; i++) change(i, v);
    }
    
    inline int ask(int l, int r, int v)
    {
        int i, res = 0, x = bel[l];
        for (i = l; i <= r; i++)
        res += val[find(i)] - tag[x] == v;
        return res;
    }
    
    inline int qsum(int x, int v)
    {
        int y = tag[x] + v;
        return y <= 1e5 ? cnt[h[x][y]] : 0;
    }
    
    inline int query(int l, int r, int v)
    { 
        int x = bel[l], y = bel[r];
        if (x == y) return ask(l, r, v);
        int i, res = ask(l, br[x], v) + ask(bl[y], r, v);
        for (i = x + 1; i < y; i++) res += qsum(i, v);
        return res;
    }
    
    int main()
    {
        int i, j, tot = 0, opt, l, r, v;
        read(n); read(m);
        s = sqrt(n);
        for (i = 1; i <= n; i++) 
        {
            read(a[i]);
            f[i] = i;
            cnt[i] = 1;
            val[i] = a[i];
        }
        for (i = 1; i <= n; i = j + 1)
        {
            j = min(n, i + s - 1);
            bl[++tot] = i;
            br[tot] = j;
            for (int k = i; k <= j; k++) bel[k] = tot;
        }
        for (i = 1; i <= tot; i++) build(i);
        while (m--)
        {
            read(opt);
            read(l);
            read(r);
            read(v);
            if (opt == 1) update(l, r, v);
            else printf("%d\n", query(l, r, v));
        }
        return 0;
    }
    
  • 相关阅读:
    一:Go编程语言规范--块、声明、作用域
    三:shell运算符
    二:shell之bash变量
    一:Shell基础
    Linux vim(4)
    二:C语言(分之结构)
    一:c语言(数据类型和运算符)
    吐槽一下百度系网站图片的一些问题
    深入理解querySelector(All)
    当fixed元素相互嵌套时chrome下父元素会影响子元素的层叠关系
  • 原文地址:https://www.cnblogs.com/cyf32768/p/12196501.html
Copyright © 2011-2022 走看看