bzoj4540(莫队+前缀和+ST表)

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


考虑右端点的贡献。
假设 l [ i ] 表示 i 左边第一个比 a [ i ] 小的位置。
那么一段区间 ( l , r ) 的贡献大概就是
i = r i >= l ( i l [ i ] ) a [ i ] ( i = l [ i ] )
这个我们可以前缀和搞出来
但是这样跳可能会跳到区间外怎么办?
我们取 ( l , r ) 最小值为 a [ k ] 那么他必定是一段数的 l [ i ] ,我们就把他设定为跳的终点。那么 a n s = s u m [ r ] s u m [ k ] + a [ k ] ( k l + 1 )
左端点贡献处理方法同理。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n , Q;
int a[101000] , L[101000] , R[101000] , belong[101000];
ll suml[101000] , sumr[101000];
int f[101000][20];
int p[101000][20];
struct data{
    int l , r , id;
}q[101000];
int read()
{
    int sum = 0;char c = getchar();bool flag = true;
    while( c < '0' || c > '9' ) {if(c == '-') flag = false;c = getchar();}
    while( c >= '0' && c <= '9' ) sum = sum * 10 + c - 48 , c = getchar();
    if(flag)  return sum;
     else return -sum;
}  
stack<int>s;
void pre()
{
    int k = sqrt(n);
    for(int i = 1,j = k,t = 1;i <= n;i += k,j += i,t++)
        if(j <= n) for(int x = i;x <= j;++x) belong[x] = t;
        else for(int x = i;x <= n;++x) belong[x] = t;
    s.push(1);L[1] = 0;
    for(int i = 2;i <= n;++i)
    {
        while(s.size() && a[i] <= a[s.top()]) s.pop();
        if(s.size()) L[i] = s.top();
        s.push(i);
    }
    for(int i = 1;i <= n;++i)
        suml[i] = suml[L[i]] + 1ll * (i - L[i]) * a[i];
    while(s.size()) s.pop();
    s.push(n);R[n] = n + 1;
    for(int i = n - 1;i >= 1;--i)
    {
        while(s.size() && a[i] <= a[s.top()]) s.pop();
        if(s.size()) R[i] = s.top();
        else R[i] = n + 1;
        s.push(i);
    }
    for(int i = n;i >= 1;--i)
        sumr[i] = sumr[R[i]] + 1ll * (R[i] - i) * a[i];
    for(int i = 1;i <= n;++i)
        f[i][0] = a[i] , p[i][0] = i;
    for(int k = 1;(1<<k) <= n;++k)
        for(int i = 1;i <= n;++i)
        {
            f[i][k] = f[i][k - 1];
            p[i][k] = p[i][k - 1];
            if(n >= (i + (1 << (k - 1))))
                if(f[i][k] > f[i + (1<<(k - 1))][k - 1])
                    p[i][k] = p[i + (1<<(k - 1))][k - 1] , f[i][k] = f[i + (1<<(k - 1))][k - 1];
        }
    return;
}
bool mycmp(data a,data b)
{
    return belong[a.l] < belong[b.l] ||
        (belong[a.l] == belong[b.l] && a.r < b.r);
}
void init()
{
    n = read();Q = read();
    for(int i = 1;i <= n;++i) a[i] = read();
    pre();
    for(int i = 1;i <= Q;++i)
        q[i].l = read() , q[i].r = read() , q[i].id = i;
    sort(q + 1,q + Q + 1,mycmp);
    return;
}
int ask(int l,int r)
{
    int k = log2(r - l + 1);
    if(f[l][k] < f[r - (1 << k) + 1][k]) return p[l][k];
    else return p[r - (1 << k) + 1][k];
}
ll changel(int l,int r)
{
    ll sum = 0;
    int k = ask(l,r);
    sum += 1ll * a[k] * (r - k + 1);
    sum += sumr[l] - sumr[k];
    return sum; 
}
ll changer(int l,int r)
{
    ll sum = 0;
    int k = ask(l,r);
    sum += 1ll * a[k] * (k - l + 1);
    sum += suml[r] - suml[k];
    return sum;
}
ll out[101000];
void solve()
{
    int l = 1,r = 0;ll now = 0;
    for(int i = 1;i <= Q;++i)
    {
        while(l > q[i].l) l-- , now += changel(l,r);
        while(r < q[i].r) r++ , now += changer(l,r);
        while(l < q[i].l) now -= changel(l,r) , l++;
        while(r > q[i].r) now -= changer(l,r) , r--;
        out[q[i].id] = now;
    }
    for(int i = 1;i <= Q;++i)
        printf("%lld\n",out[i]);
    return;
}
int main() 
{
    init();
    solve();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/a1035719430/article/details/81108518