bzoj4540: [Hnoi2016]序列 莫队算法

bzoj4540: [Hnoi2016]序列

Description

  给定长度为n的序列:a1,a2,…,an,记为a[1:n]。类似地,a[l:r](1≤l≤r≤N)是指序列:al,al+1,…,ar-
1,ar。若1≤l≤s≤t≤r≤n,则称a[s:t]是a[l:r]的子序列。现在有q个询问,每个询问给定两个数l和r,1≤l≤r
≤n,求a[l:r]的不同子序列的最小值之和。例如,给定序列5,2,4,1,3,询问给定的两个数为1和3,那么a[1:3]有
6个子序列a[1:1],a[2:2],a[3:3],a[1:2],a[2:3],a[1:3],这6个子序列的最小值之和为5+2+4+2+2+2=17。

Input

  输入文件的第一行包含两个整数n和q,分别代表序列长度和询问数。接下来一行,包含n个整数,以空格隔开
,第i个整数为ai,即序列第i个元素的值。接下来q行,每行包含两个整数l和r,代表一次询问。

Output

  对于每次询问,输出一行,代表询问的答案。

Sample Input

5 5
5 2 4 1 3
1 5
1 3
2 4
3 5
2 5

Sample Output

28
17
11
11
17

HINT

1 ≤N,Q ≤ 100000,|Ai| ≤ 10^9

分析

这道题好像有 O ( n l o g n ) 本蒟蒻不会。。
于是打了莫队。
莫队就考虑增量,以 [ l , r ] [ l , r + 1 ] 为例,往左扩展是类似的。
新增的区间是 [ l , r + 1 ] , [ l + 1 , r + 1 ] [ r + 1 , r + 1 ]
假设这个区间内的最小值的位置是 p
那么 [ l , r + 1 ] , [ l + 1 , r + 1 ] [ p , r + 1 ] 的贡献已经确定。
剩下的区间贡献和原来的贡献一样。考虑预处理。
假设某个位置 i ,仅仅考虑它往右的贡献往右第一个比它大的位置是 r [ i ]
那么这个东西的贡献是 ( r [ i ] i ) a [ i ]
用类似前缀和的方式维护 ( r [ i ] i ) a [ i ]
得到方程 f r [ i ] = f r [ r [ i ] ] + ( r [ i ] i ) a [ i ]
f r [ p ] f r [ l ] 即可得到剩余区间的答案。
倒过来想,这里找最小值这一步实质上是为了防止 f r [ i ] 覆盖到超出右端点的贡献。
左端点类似。
用st表和单调栈预处理即可。

代码

#include<cstdio>
#include<algorithm>
#include<cmath>
const int N = 1e5 + 10;
long long read() {
    char ch = getchar(); long long x = 0, f = 1;
    for(;ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;
    for(;ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) - '0' + ch;
    return x * f;
}
int Log[N], a[N], st[N], r[N], l[N], be[N], mn[20][N], bin[20], n, top, L, R, blo;
long long dl[N], dr[N], ans[N], ret;
struct data {int l, r, id;}q[N];
bool cmp(data a, data b) {return be[a.l] == be[b.l] ? a.r < b.r : a.l < b.l;}
int bet(int x, int y) {return a[x] < a[y] ? x : y;}
void Pre() {
    bin[0] = 1; for(int i = 1;i <= 19; ++i) bin[i] = bin[i - 1] << 1;
    Log[0] = -1; for(int i = 1;i <= n; ++i) Log[i] = Log[i >> 1] + 1;
    for(int i = 1;i <= n; ++i) {
        while(top && a[st[top]] > a[i]) r[st[top--]] = i;
        l[i] = a[st[top]] == a[i] ? l[st[top]] : st[top];
        st[++top] = i;
    }
    for(;top ; r[st[top--]] = n + 1) ;
    for(int i = 1;i <= n; ++i) dl[i] = dl[l[i]] + 1LL * (i - l[i]) * a[i];
    for(int i = n; i; --i) dr[i] = dr[r[i]] + 1LL * (r[i] - i) * a[i];
    for(int i = 1;i <= Log[n]; ++i)
        for(int j = 1; j <= n - bin[i] + 1; ++j)
            mn[i][j] =  bet(mn[i - 1][j], mn[i - 1][j + bin[i - 1]]);
}
int Que() {
    int t = Log[R - L + 1];
    return bet(mn[t][L], mn[t][R - bin[t] + 1]);
}
void UpL(long long f) {
    int p = Que();
    ret += f * (R - p + 1) * a[p];
    ret += f * (dr[L] - dr[p]);
}
void UpR(long long f) {
    int p = Que();
    ret += f * (p - L + 1) * a[p];
    ret += f * (dl[R] - dl[p]);
}
int main() {
    n = read(); int m = read();
    for(int i = 1;i <= n; ++i) mn[0][i] = i, a[i] = read();
    Pre(); blo = sqrt(n); for(int i = 1;i <= n; ++i) be[i] = i / blo;
    for(int i = 1;i <= m; ++i)
        q[i].l = read(), q[i].r = read(), q[i].id = i;
    std::sort(q + 1, q + m + 1, cmp); 
    L = R = 1; ret = a[L];
    for(int i = 1;i <= m; ++i) {
        while(R > q[i].r) UpR(-1), --R;
        while(R < q[i].r) ++R, UpR(1);
        while(L > q[i].l) --L, UpL(1);
        while(L < q[i].l) UpL(-1), ++L;
        ans[q[i].id] = ret;
    }
    for(int i = 1;i <= m; ++i) printf("%lld\n", ans[i]); puts(""); getchar(); getchar();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lvzelong2014/article/details/80024500