BZOJ4540: [Hnoi2016]序列

BZOJ4540: [Hnoi2016]序列

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


题解Here!

 

字符串做得要吐了,于是又回来搞大力数据结构。。。
然后发现还是数据结构好写555。。。
这个题反正$RMQ$跑不了。
然后就是怎么求子区间的和了。
没有强制在线,我们可以拿出区间神器——莫队
莫队的难点就在于怎么由区间$[l,r]$推出$[l,r+1]$。
考虑他们之间的增加量就是新增的区间$[l,r+1],[l+1,r+1],[l+2,r+1],...,[r+1,r+1]$的最小值之和。
一共$r-l+2$个区间。
如果我们知道区间$[l,r+1]$的最小值位置为$pos$,那么左端点在区间$[l,pos]$之前的区间答案都是$val[pos]$。
对答案的贡献就是$val[pos]\times (pos-l+1)$。
至于$pos$怎么求,当然用$O(1)$的$ST$表啦。。。
只不过$ST$表维护的是最小值的位置。
再考虑剩下的左端点在$[pos+1,r+1]$之间的区间。
这里引入一个类似前缀和的东西。
我们用$front[i]$表示$i$左边第一个比$val[i]$小的数的位置,$next[i]$表示右边。
这个过程显然可以用单调栈$O(n)$预处理。
然后开2个数组$maxl,maxr$。
$maxl[i]$表示$[1,i]$的贡献,那么就有:$maxl[i]=maxl[front[i]]+(i-front[i])*val[i]$
$maxr$同理。
这样,我们就可以在$O(1)$时间内转移了。
那么,从$[l,r]$到$[l,r-1]$呢?
我们只要减去$[l,r-1]->[l,r]$的贡献即可。
从$[l,r]$到$[l+1,r]$与从$[l,r]$到$[l-1,r]$就利用对称性求解即可。
然后我们就可以愉快地跑莫队了。
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
#define MAXN 100010
using namespace std;
int n,m;
int val[MAXN],f[MAXN][30],front[MAXN],next[MAXN];
long long ans[MAXN],maxl[MAXN],maxr[MAXN];
struct node{
	int l,r,id;
}que[MAXN];
struct Stack{
    int value[MAXN],numtop;
    Stack(){
        memset(value,0,sizeof(value));
        numtop=0;
    }
    inline void push(int x){value[++numtop]=x;}
    inline void pop(){value[numtop--]=0;}
    inline int top(){return value[numtop];}
    inline int empty(){return (numtop==0?1:0);}
}stack;
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
bool cmp1(const node &x,const node &y){
	return x.l<y.l;
}
bool cmp2(const node &x,const node &y){
	if(x.r==y.r)return x.l<y.l;
	return x.r<y.r;
}
void step(){
    for(int i=1;(1<<i)<=n;i++)
    for(int j=1;j+(1<<i)-1<=n;j++){
    	if(val[f[j][i-1]]>val[f[j+(1<<(i-1))][i-1]])f[j][i]=f[j+(1<<(i-1))][i-1];
    	else f[j][i]=f[j][i-1];
    }
}
int query(int l,int r){
    int k=0;
    while((1<<(k+1))<=r-l+1)k++;
	if(val[f[l][k]]>val[f[r-(1<<k)+1][k]])return f[r-(1<<k)+1][k];
    return f[l][k];
}
void work(){
    int left=1,right=0;
	long long s=0;
	for(int i=1;i<=m;i++){
		while(right<que[i].r)
		{
			right++;
			int pos=query(left,right);
			s+=(long long)val[pos]*(pos-left+1)+maxl[right]-maxl[pos];
		}
		while(que[i].l>left)
		{
			int pos=query(left,right);
			s-=(long long)val[pos]*(right-pos+1)+maxr[left]-maxr[pos];
			left++;
		}
		while(right>que[i].r)
		{
			int pos=query(left,right);
			s-=(long long)val[pos]*(pos-left+1)+maxl[right]-maxl[pos];
			right--;
		}
		while(que[i].l<left)
		{
			left--;
		    int pos=query(left,right);
		    s+=(long long)val[pos]*(right-pos+1)+maxr[left]-maxr[pos];
		}
		ans[que[i].id]=s;
	}
	for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
}
void init(){
	int nowi=1,nowj=0,d,x;
    n=read();m=read();
    for(int i=1;i<=n;i++){
        val[i]=read();
        f[i][0]=i;
    }
    step();
	for(int i=1;i<=m;i++){
		que[i].l=read();que[i].r=read();
		que[i].id=i;
	}
	x=sqrt(n);
	sort(que+1,que+m+1,cmp1);
	while(nowi<=m){
		nowj++;
		d=nowi;
		while(que[nowi].l<nowj*x&&nowi<=m)nowi++;
		sort(que+d,que+nowi,cmp2);
		if(nowj==x){
			sort(que+d,que+m+1,cmp2);
			break;
		}
	}
	for(int i=1;i<=n;i++){
	    for(;!stack.empty()&&val[i]<val[stack.top()];stack.pop())next[stack.top()]=i;
	    front[i]=stack.top();
	    stack.push(i);
	}
	for(;!stack.empty();stack.pop())next[stack.top()]=n+1;
	for(int i=1;i<=n;i++)maxl[i]=maxl[front[i]]+(long long)val[i]*(i-front[i]);
	for(int i=n;i>=1;i--)maxr[i]=maxr[next[i]]+(long long)val[i]*(next[i]-i);
}
int main(){
    init();
    work();
	return 0;
}

 

posted @ 2018-08-11 15:42  符拉迪沃斯托克  阅读(384)  评论(0编辑  收藏  举报
Live2D