卧薪尝胆,厚积薄发。
HNOI2016 序列
Date: Sun Nov 04 13:09:44 CST 2018 In Category: NoCategory

Description:

给一个长为 $n$ 的序列, $q$ 次询问某个区间的所有子区间的最小值之和。
$1\leqslant n\leqslant 10^5$

Solution:

根本没有什么思路,直接转述题解。
设询问的区间为 $[l,r]$ ,这个区间最小值的位置是 $p$ ,那么对于所有左端点在 $[l,p]$ ,右端点在 $[p,r]$ 的区间最小值都是 $p$ ,这部分对答案的贡献是 $(p-l+1)\times(r-p+1)\times a[p]$ ,这时还需要统计左右端点都在 $[l,p)$ 或 $(p,r]$ 的答案,设 $F[l][r]$ 表示所有左端点在 $[l,r]$ 右端点在 $r$ 的答案,设 $pre[i]$ 为 $i$ 之前第一个比 $i$ 小的位置,所有以 $[pre[i]+1,r]$ 开头以 $r$ 结尾的最小值都是 $a[r]$ ,因此有 $F[l][r]=F[l][pre[r]]+(r-pre[r])\times a[r]$ ,但是这个状态是 $O(n^2)$ 的,考虑如何优化,发现转移跟 $l$ 没啥关系,考虑去掉一维,只用 $f[i]$ 来表示是否可行,因为 $p$ 是 $[l,r]$ 中最小的数字,所以一定 $\exist x\in(p,r],pre[x]=p$ ,也就是说 $f[r]=a[r]\times (r-pre[r])+a[pre[r]]\times(pre[r]-pre[pre[r]])+\cdots+a[x]\times (x-p)+f[p]$ ,那么 $f[r]-f[p]$ 就是 $F[p+1][r]$ ,那么我们只要把 $f[i]$ 做个前缀和就可以得到 $[p+1,r]$ 中的所有 $f[i]$ 每个都减掉 $f[p]$ 就处理完了右半部分,左半部分同理。

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<stack>
#include<cctype>
#include<cstring>
using namespace std;
int n;
#define MAXN 100010
int a[MAXN];
int m;
typedef long long ll;
int pre[MAXN],nxt[MAXN];
ll fl[MAXN],fr[MAXN];
ll sfl[MAXN],sfr[MAXN];
int f[MAXN][20];
int my_log2[MAXN];
int calc(int l,int r)
{
int len = my_log2[r - l + 1] - 1;
if(a[f[l][len]] < a[f[r - (1 << len) + 1][len]])return f[l][len];
else return f[r - (1 << len) + 1][len];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;++i)scanf("%d",&a[i]);
a[0] = a[n + 1] = 0xc0c0c0c0;
stack<int> s;s.push(0);
for(int i = 1;i <= n;++i)
{
while(a[s.top()] >= a[i])s.pop();
pre[i] = s.top();
s.push(i);
}
while(!s.empty())s.pop();s.push(n + 1);
for(int i = n;i >= 1;--i)
{
while(a[s.top()] >= a[i])s.pop();
nxt[i] = s.top();
s.push(i);
}
for(int i = 1;i <= n;++i)fr[i] = fr[pre[i]] + 1ll * (i - pre[i]) * a[i];
for(int i = n;i >= 1;--i)fl[i] = fl[nxt[i]] + 1ll * (nxt[i] - i) * a[i];
for(int i = n;i >= 1;--i)sfl[i] = sfl[i + 1] + fl[i];
for(int i = 1;i <= n;++i)sfr[i] = sfr[i - 1] + fr[i];
for(int i = 1;i <= n;++i)f[i][0] = i;
for(int k = 1;k <= 19;++k)
{
for(int i = 1;i <= n - (1 << k) + 1;++i)
{
if(a[f[i][k - 1]] < a[f[i + (1 << (k - 1))][k - 1]])f[i][k] = f[i][k - 1];
else f[i][k] = f[i + (1 << (k - 1))][k - 1];
}
}
for(int i = 1;i <= n;++i)my_log2[i] = my_log2[i >> 1] + 1;
int l,r;
for(int i = 1;i <= m;++i)
{
scanf("%d%d",&l,&r);
int p = calc(l,r);
ll ans1 = sfr[r] - sfr[p] - 1ll * (r - p) * fr[p];
ll ans2 = sfl[l] - sfl[p] - 1ll * (p - l) * fl[p];
printf("%lld\n",ans1 + ans2 + 1ll * (r - p + 1) * (p - l + 1) * a[p]);
}
return 0;
}
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡