zoukankan      html  css  js  c++  java
  • bzoj4540

    莫队+st表

    据说这是经典问题,但是我不会。。。

    问题在于莫队怎么算贡献,每次移动一个位置,现在为[l,r],那么就增加了[l-1,r),r的贡献,怎么算呢?我们预处理fl,fr,fl[i]表示以i为开头的前缀和,fr表示以i为结尾的后缀和,这个东西能够相减,但也不是完全满足

    每次我们计算贡献的时候,设最小值的位置为p,那么对于右端点来说,贡献就是fr[r]-fr[p]+(p - l + 1) * a[p],这也是因为不完全满足可减性,因为求前缀和是fr[i]=(i - l[i] + 1) * a[i] + fr[l[i]-1],那么满足可减性是在每个l[i]的时候可以减,又因为p作为最小值肯定是处于一个l的位置,那么就能减了,而到l的贡献就是p作为最小值。

    理解得还不是很透彻 我一直以为我的单调栈是左闭右闭的,竟然是左开右闭的

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 5;
    int n, m, block, l = 1, r, top;
    ll sum;
    int st[N], Log[N], f[N][18], L[N], R[N];
    ll fl[N], fr[N], a[N], ans[N];
    struct query {
        int l, r, id;
        bool friend operator < (const query &a, const query &b) {
            return (a.l - 1) / block == (b.l - 1) / block ? a.r < b.r : (a.l - 1) / block < (b.l - 1) / block; 
        }
    } q[N];
    inline int rd()
    {
        int x = 0, f = 1; char c = getchar();
        while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
        while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
        return x * f;
    } 
    int Min(int x, int y)
    {
        return a[x] < a[y] ? x : y;
    }
    int rmq(int l, int r)
    {
        int x = Log[r - l + 1];
        return Min(f[l][x], f[r - (1 << x) + 1][x]);
    }
    void addl(int l, int r, ll f)
    {
        int p = rmq(l, r);
        sum += f * (fl[l] - fl[p] + a[p] * (ll)(r - p + 1));
    }
    void addr(int l, int r, ll f)
    {
        int p = rmq(l, r);
        sum += f * (fr[r] - fr[p] + a[p] * (ll)(p - l + 1));
    }
    int main()
    {
        n = rd();
        m = rd();
        block = sqrt(n);
        for(int i = 1; i <= n; ++i) 
        {
            a[i] = rd();
            f[i][0] = i;
        }
        for(int i = 2; i <= n; ++i) Log[i] = Log[i >> 1] + 1;
        for(int j = 1; j <= 17; ++j)
            for(int i = 1; i + (1 << j) <= n + 1; ++i) 
                f[i][j] = Min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
        for(int i = 1; i <= m; ++i) 
        {
            q[i].l = rd();
            q[i].r = rd();
            q[i].id = i;
        }   
        sort(q + 1, q + m + 1); 
        for(int i = 1; i <= n; ++i)
        {
            L[i] = R[i] = i;
            while(top && a[st[top]] > a[i]) 
            {
                R[st[top - 1]] = R[st[top]];
                L[i] = L[st[top]];
                --top;
            }
            st[++top] = i;
        }
        while(top) 
        {
            R[st[top - 1]] = R[st[top]];
            --top;
        } 
        for(int i = 1; i <= n; ++i) fr[i] = fr[L[i] - 1] + (ll)(i - L[i] + 1) * a[i];
        for(int i = n; i; --i) fl[i] = fl[R[i] + 1] + (ll)(R[i] - i + 1) * a[i];
        l = 1;
        r = 0;
        for(int i = 1; i <= m; ++i) 
        {
            while(r < q[i].r) addr(l, ++r, 1);
            while(r > q[i].r) addr(l, r--, -1);
            while(l < q[i].l) addl(l++, r, -1);
            while(l > q[i].l) addl(--l, r, 1);       
            ans[q[i].id] = sum;
        }
        for(int i = 1; i <= m; ++i) printf("%lld
    ", ans[i]);
        return 0;
    }
    
    View Code

    莫队一定要先动r

  • 相关阅读:
    greybox关闭/刷新父窗口
    C# 获取文件编码
    框架页,URL中文参数乱码
    用来代替SQLSERVERAGENT的VBS脚本。
    jQuery的radio,checkbox,select操作
    mssql 的sp_help好难看
    如何判断网通、电信、铁通IP地址分配段
    IE8取不到 select 的option值
    如何识别当前的 SQL Server 版本号以及对应的产品级别
    控诉我的电脑
  • 原文地址:https://www.cnblogs.com/19992147orz/p/7927934.html
Copyright © 2011-2022 走看看