[HNOI2016]序列 草稿纸

P3246 [HNOI2016]序列
给定一个长度为n的序列,q个询问,每次询问[l,r]的所有子段的最小值之和。

设f[i]为以i为右端点的区间答案,pre[i]为i左边第一个比a[i]小的数的位置。 可以发现如果这个左端点<=pre[i],其实右端点放在pre[i]和放在i答案是一样的。
所以有\(f[i]=a[i]*(i-pre[i])+f[pre[i]]\)
\(=>f[i]-f[pre[i]]=a[i]*(i-pre[i])\)(其实是左端点在pre[i]+1~i之间,右端点在i的答案)
又或者\(f[i]=a[i]*(i-pre[i])+a[pre[i]]*(pre[i]-pre[pre[i]])+f[pre[pre[i]]]\)
\(=>f[i]-f[pre[pre[i]]]=(f[i]-f[pre[i]])+(f[pre[i]]-f[pre[pre[i]]])=a[i]*(i-pre[i])+a[pre[i]]*(pre[i]-pre[pre[i]])\)(其实是左端点在pre[pre[i]]+1~i之间,右端点在i的答案)(这样一定可以处理左端点为i左边一个比i小的数到i这个区间的答案)

然后就考虑左端点在\([l,r]\),右端点在\(r+1\)的答案....
那么其实可以p=r+1; while (pre[p]>=l) p=pre[p]; 其实发现这样最后得到的p就是[l,r+1]的最小值位置,用RMQ可以O1或Ologn求出
r+1的时候答案的增量计算分为两部分
对于左端点在\([p+1,r+1]\),右端点在\(r+1\)的部分,答案就为\(f[r+1]-f[p]\)
对于左端点在\([l,p]\),右端点在\(r+1\)的部分,答案就为\(a[p]*(p-l+1)\)
这样可以离线询问Onsqrtn莫队完成

在线怎么写(
直接考虑一个区间\([l,r]\)的答案
w...怎么搞呢
考虑右端点在r的答案...同样找出一个最小值位置p。
那么左端点在\([p+1,r]\)的答案是\(f[r]-f[p]\)

右端点在\(r-1\),左端点在\([p+1,r-1]\)的答案是\(f[r-1]-f[p]\)
那左端点在\([p+1,右端点]\),右端点在\([p+1,r]\),也即\([p+1,r]\)区间内的答案为 \(( \Sigma_{i = p+1}^{r} f[i] ) - f[p]*(r-p)\)
对f做个前缀和数组c 答案就是 \(c[r]-c[p]-f[p]*(r-p)\)

\([l,p-1]\)区间内的答案,可以倒过来求的样子(
答案是 \(c[p-1]-c[l-1]-f[p]*(p-l)\)

覆盖p的区间答案,\(a[p]*(p-l+1)*(r-p+1)\)

QAQ 完结....居然写了这么久思路才理清楚

code:
一遍过样例,爆long long 喜提0pts,暴躁kkz在线#define int long long

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
#define MAXN (int)(1e5+233)
#define MAXA (int)(1e9+233)
#include <stack>
struct qwq
{
	long long a,id;
};
stack<qwq> st;
#define int long long
int a[MAXN];
int n,q;

int prer[MAXN],prel[MAXN];



inline void mostack()
{
	for (int i=1;i<=n;i++)
	{
		while (!st.empty()&&st.top().a>a[i])
		{
			prer[st.top().id]=i;
			st.pop();
		}
		st.push((qwq){a[i],i});
	}
	while (!st.empty()) st.pop();
	for (int i=n;i;i--)
	{
		while (!st.empty()&&st.top().a>a[i])
		{
			prel[st.top().id]=i;
			st.pop();
		}
		st.push((qwq){a[i],i});
	}
	return;
}
int ans[MAXN<<2];
#define leftson cur<<1
#define rightson cur<<1|1
#define mid ((l+r)>>1)
#define push_up if (a[ans[leftson]]<a[ans[rightson]]) ans[cur]=ans[leftson]; else ans[cur]=ans[rightson]
void build(int cur,int l,int r)
{
	if (l==r)
	{
		ans[cur]=l;
		return;
	}
	build(leftson,l,mid);
	build(rightson,mid+1,r);
	push_up;
}
int query(int ql,int qr,int cur,int l,int r)
{
	if (ql<=l&&r<=qr) return ans[cur];
	int answer=0;
	if (ql<=mid) answer=query(ql,qr,leftson,l,mid);
	if (qr>mid) { int tt=query(ql,qr,rightson,mid+1,r); if (!answer) answer=tt; else answer=(a[tt]>a[answer]?answer:tt); }
	return answer;
}

inline void RMQINIT() { build(1,1,n); return; }
long long fl[MAXN],cl[MAXN],fr[MAXN],cr[MAXN];
inline void FINIT()
{
	for (int i=1;i<=n;i++)
		fl[i]=a[i]*(i-prel[i])+fl[prel[i]];
	for (int i=n;i;i--)
		fr[i]=a[i]*(prer[i]-i)+fr[prer[i]];
	for (int i=1;i<=n;i++)
		cl[i]=cl[i-1]+fl[i];
	for (int i=n;i;i--)
		cr[i]=cr[i+1]+fr[i];
	return;
}

signed main()
{
	scanf("%lld%lld",&n,&q);
	for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
	mostack();
	RMQINIT();
	FINIT();
	int l,r,p;
	long long ans=0;
	while (q--)
	{
		scanf("%lld%lld",&l,&r);
		p=query(l,r,1,1,n);
		ans=0;
		if (p+1<=r) ans=cl[r]-cl[p]-fl[p]*(r-p);
		if (p-1>=l) ans+=cr[l]-cr[p]-fr[p]*(p-l);
		ans+=a[p]*(p-l+1)*(r-p+1);
		printf("%lld\n",ans);
	}
	return 0;
}