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\n", ans[i]);
    return 0;
}
View Code

莫队一定要先动r

posted @ 2017-11-30 10:56  19992147  阅读(178)  评论(0编辑  收藏  举报