Bzoj4540:[Hnoi2016]序列:莫队+RMQ

题目链接4540:[Hnoi2016]序列

考虑莫队算法,在移动一个端点的时候,假设要处理的区间为[l,r]切向左扩大区间,最小值所在位置为k,那么a[k]的贡献为(r-k+1)*a[k],即[k,r]的新增序列都以a[k]为最小值

还有一段区间[l,k-1]没有处理,设s[i]为以i为左端点的合法区间的答案,r[i]为i右侧第一个不大于a[i]的输的位置,那么s[i]=s[r[i]]+1ll*(r[i]-i)*a[i],即在[i,r[i]-1]都以a[i]为最小值

[i+1,r[i]-1]中的数一定大于a[i],所以[i,r[i]-1]中的数一定以r[i]为转移点,即s[i]>s[[i+1,r[i]-1]]中的任何一个,也就表明s[]具有前缀和性质,所以[l,k-1]区间的答案为s[l]-s[k];

区间最小值位置用RMQ O(nlogn)预处理,O(1)查询即可

#include
#include
#include
#include
#include
#define ll long long
using namespace std;
const int maxn=100010;
struct point{int l,r,id;}p[maxn];
int n,m,l[maxn],a[maxn],r[maxn],top=0,po[maxn];
int sta[maxn],pos[maxn],mi[maxn][19],lg[maxn];
ll s1[maxn],s2[maxn],nowans=0,ans[maxn];

void update(int &x,int y,int z){
	a[y]r) swap(l,r);
	int len=lg[r-l+1];
	return a[mi[l][len]]>1]+1,pos[i]=(i-1)/blo+1;
	for (int i=1;i<=18;++i) po[i]=po[i-1]<<1;
	for (int i=1;i<=n;++i) mi[i][0]=i;
	for (int j=1;j<=18;++j)
	    for (int i=1;i<=n;++i){
			mi[i][j]=mi[i][j-1];
			if (i+po[j-1]<=n) update(mi[i][j],mi[i][j-1],mi[i+po[j-1]][j-1]);
	    }	
	for (int i=1;i<=n;++i){
		while (top&&a[sta[top]]>=a[i]) r[sta[top--]]=i;
		sta[++top]=i;
	}
	while (top) r[sta[top--]]=n+1;
	for (int i=n;i>=1;--i){
		while (top&&a[sta[top]]>a[i]) l[sta[top--]]=i;
		sta[++top]=i;
	}
	while (top) l[sta[top--]]=0;
	for (int i=1;i<=n;++i) s1[i]=s1[l[i]]+1ll*(i-l[i])*a[i];
	for (int i=n;i>=1;--i) s2[i]=s2[r[i]]+1ll*(r[i]-i)*a[i];
	sort(p+1,p+m+1,cmp);
	int L=1,R=1; nowans=a[1];
	for (int i=1;i<=m;++i){
		while (Rp[i].l) change1(L-1,R,1),L--;
		while (R>p[i].r) change2(L,R,-1),R--;
		while (L


你可能感兴趣的:(OI,莫队算法,RMQ)