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] l [ i ] 表示 i i 左边第一个比 a[i] a [ i ] 小的位置。
那么一段区间 (l,r) ( l , r ) 的贡献大概就是
∑i>=li=r(i−l[i])∗a[i](i=l[i]) ∑ i = r i >= l ( i − l [ i ] ) ∗ a [ i ] ( i = l [ i ] )
这个我们可以前缀和搞出来
但是这样跳可能会跳到区间外怎么办?
我们取 (l,r) ( l , r ) 最小值为 a[k] a [ k ] 那么他必定是一段数的 l[i] l [ i ] ,我们就把他设定为跳的终点。那么 ans=sum[r]−sum[k]+a[k]∗(k−l+1) a n s = s u m [ r ] − s u m [ k ] + a [ k ] ∗ ( k − l + 1 )
左端点贡献处理方法同理。
#include
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<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;
}