• [HNOI 2016] 序列


    题目

    传送门

    解法

    妙妙题。


    可以先考虑只有一个询问,询问区间为 ([1,n]) 该怎么做。不过这个和正解没什么关系,可以跳过。

    显然对于每种权值 (k),用单调队列求出距离其下标最近的两个比它小的权值,这中间夹的就是贡献为 (k) 的区间。

    时间复杂度 (mathcal O(n))


    不过这道题不行。因为当 (l,r) 改变时,区间内每种权值都需要维护,时间就炸了。

    试试莫队?

    既然是莫队,我们就应该先考虑如何向外拓展。先假设原区间为 ([l,r]),我们拓展至 (r+1)

    那么答案就是 (∑min{[i,r+1]} (lle ile r+1))

    (f[i]) 为以 (i) 为结尾的所有区间的 (min) 之和。

    (f[i]) 挺好算,我们找到 (i) 前面第一个比 (a[i]) 小的数的下标记为 (pos)。那么递推式就是:

    [f[i]=f[pos]+a[i] imes (i-pos) ]

    关于 (pos),用单调递增队列维护即可。

    我们找出 ([l,r]) 中最小值的下标 (p),类似上面的 (mathtt{DP}) 就有

    [Delta=a[p] imes (p-l+1)+f[r]-f[p] ]

    由于 (a[p])([l,r]) 中最小值,所以 ((p,r]) 区间内的值对包含位置 (p) 的区间没有任何影响,也即没有改变。所以剩下的就正好是左端点在 ((p,r]) 的区间的答案。

    类似地,我们设 (g[i]) 为以 (i) 为开始的所有区间的 (min) 之和。这里就不再赘述。

    时间复杂度 (mathcal O(nsqrt n))

    接下来我们谈谈莫队玄学的坑。

    • 首先四个循环时我们应该先加 (R),再加 (L);先减 (L),再减 (R)。不然可能会出现 (L>R) 的悲惨情况。尤其是莫队的奇偶优化,有时循环打错了会造成朴素莫队过得了,优化过不了的情况。(应该是优化使得右端点回退次数多了,造成 (L>R)
    • 有些题虽然可以用 long long 输出,但是中途莫队可能会爆,所以满足上述操作时,保险将加值与减值轮换操作。

    总结

    观察上面两种方法,其实就是因为限制有 权值区间,枚举其中一项,尝试快速获得另一项。

    代码终于来辽!

    代码

    #include <cmath>
    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    
    const int N = 1000100, lim = 2e9;
    
    int rmq[N][22], lg[N], q[N], n, m, a[N], pos[N], block, minn[N][2];
    ll ans, cnt[N], res[N], f[N][2];
    struct node {
        int l, r, id;
    }s[N];
    
    int read() {
        int x = 0, f = 1; char s;
        while((s = getchar()) > '9' || s < '0') if(s == '-') f = -1;
        while(s >= '0' && s <= '9') {x = (x << 1) + (x << 3) + (s ^ 48); s = getchar();}
        return x * f;
    }
    
    int Cmp(const int x, const int y) {return a[x] <= a[y] ? x : y;}
    
    bool cmp(const node a, const node b) {return (pos[a.l] ^ pos[b.l]) ? a.l < b.l : ((pos[a.l] & 1) ? a.r < b.r : a.r > b.r);}
    
    int get(const int l, const int r) {
        int len = r - l + 1;
        return Cmp(rmq[l][lg[len]], rmq[r - (1 << lg[len]) + 1][lg[len]]);
    }
    
    void rig(const int l, const int r, const ll op) {
        int p = get(l, r);
        ans += 0ll + op * (p - l + 1) * a[p] + op * (f[r][0] - f[p][0]);
    }
    
    void lef(const int l, const int r, const ll op) {
        int p = get(l, r);
        ans += 0ll + op * (r - p + 1) * a[p] + op * (f[l][1] - f[p][1]);
    }
    
    void MD() {
        int L = 1, R = 0;
        for(int i = 1; i <= m; ++ i) {
            while(R < s[i].r) rig(L, R + 1, 1ll), ++ R;
            while(L < s[i].l) lef(L, R, -1ll), ++ L;
            while(L > s[i].l) lef(L - 1, R, 1ll), -- L;
            while(R > s[i].r) rig(L, R, -1ll), -- R;
            res[s[i].id] = ans;
        }
    }
    
    void init() {
        block = sqrt(n);
        for(int i = 1; i <= n; ++ i) pos[i] = (i - 1) / block + 1;
        int l = 1, r = 1; a[0] = a[n + 1] = -lim;
        for(int i = 1; i <= n; ++ i) {
            while(l <= r && a[q[r]] >= a[i]) -- r;
            minn[i][0] = q[r];
            q[++ r] = i;
        }
        l = 1, r = 1; q[1] = n + 1;
        for(int i = n; i >= 1; -- i) {
            while(l <= r && a[q[r]] >= a[i]) -- r;
            minn[i][1] = q[r];
            q[++ r] = i;
        }
        for(int i = 1; i <= n; ++ i) f[i][0] = f[minn[i][0]][0] + 1ll * a[i] * (i - minn[i][0]);
        for(int i = n; i >= 1; -- i) f[i][1] = f[minn[i][1]][1] + 1ll * a[i] * (minn[i][1] - i);
        for(int i = 2; i <= n; ++ i) lg[i] = lg[i >> 1] + 1;
        for(int i = 1; i <= n; ++ i) rmq[i][0] = i;
        for(int j = 1; j <= 20; ++ j)
            for(int i = 1; i <= n; ++ i)
                rmq[i][j] = Cmp(rmq[i][j - 1], rmq[i + (1 << j - 1)][j - 1]);
    }
    
    int main() {
        n = read(), m = read();
        for(int i = 1; i <= n; ++ i) a[i] = read();
        for(int i = 1; i <= m; ++ i) s[i].l = read(), s[i].r = read(), s[i].id = i;
        init();
        sort(s + 1, s + m + 1, cmp);
        MD();
        for(int i = 1; i <= m; ++ i) printf("%lld
    ", res[i]);
        return 0;
    }
    
    
  • 相关阅读:
    js中的replace 回车符
    HttpHandler 读取Session
    [原]vs2005 修改新增文件时的默认编码方式
    系统托盘NotifyIcon控件及右键菜单功能
    addAttribute
    gpibCmd
    deviceFactory
    string is
    TitledMotorEntry TitledFrame MotorViewEntry TitleMotorEntry
    IOC port
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/12467870.html
Copyright © 2020-2023  润新知