4540: [Hnoi2016]序列
Description
给定长度为 的序列: , ,…, ,记为 。类似地, ( )是指序列: , ,…, , 。若 ,则称 是 的子序列。现在有 个询问,每个询问给定两个数 和 , ,求 的不同子序列的最小值之和。例如,给定序列5,2,4,1,3,询问给定的两个数为1和3,那么 有6个子序列 ,这6个子序列的最小值之和为5+2+4+2+2+2=17。
Input
输入文件的第一行包含两个整数 和 ,分别代表序列长度和询问数。接下来一行,包含 个整数,以空格隔开,第 个整数为 ,即序列第 个元素的值。接下来 行,每行包含两个整数 和 ,代表一次询问。
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
解:
听说可以大力上线段树?可是我不会啊。
其实莫队也不怎么会。
还是说说怎么做吧。首先莫队的方法可以把这个问题变成对于区间[l,r],以r为右端点,再求答案。而且由于莫队的复杂度,我们需要做到
怎么求答案?本来想的是存一个每个点左右比它小的第一个数,结果发现似乎变成了一个递归的子问题,感觉还是很无解。
然后在网上看到一种很巧妙的做法。我们现在有区间[l,r]的答案,向左扩展一位。首先我们找出[l,r+1]中最小的数k,k左边作为左端点那么对答案有贡献的是k。然后我们需要计算[k+1,r],发现一个特点,[k+1,r]中比k都大。我们预处理以i为右端点的区间的答案和s[i]。由于k及k左侧的贡献都会是k左边的数,所以在s[k+1]和s[r]是可以相减的。我们只需要减一减就好了。
RMQ是O(1)的。
莫队可能不支持除100,调成200就过了,不知道为什么。
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
using namespace std;
struct lxy{
int id,x;
}yyy;
struct lxy1{
int l,r,tim;
bool operator < (const lxy1 &QAQ)const{
if(l/200==QAQ.l/200) return r<QAQ.r;
return l/200<QAQ.l/200;
}
}b[100005];
int l[100005],r[100005],a[100005];
long long fl[100005],fr[100005],now,ans[100005];
int st[200005][17],lg[100005];
int n,m,lnt,rnt;
stack <lxy> d;
int readit()
{
int g=0,f=1;char s;
s=getchar();
while(s>'9'||s<'0'){
if(s=='-') f=-1;
s=getchar();
}
while(s>='0'&&s<='9'){
g=g*10+s-'0';
s=getchar();
}
return f*g;
}
int findit(int x,int y)
{
int g=lg[y-x+1];
if(a[st[x][g]]>a[st[y-(1<<g)+1][g]])
return st[y-(1<<g)+1][g];
else return st[x][g];
}
void lml(){
int pos=findit(lnt-1,rnt);
now+=1ll*a[pos]*(rnt-pos+1)+fr[lnt-1]-fr[pos];
lnt--;
}
void lmr(){
lnt++;
int pos=findit(lnt-1,rnt);
now-=1ll*a[pos]*(rnt-pos+1)+fr[lnt-1]-fr[pos];
}
void rml(){
rnt--;
int pos=findit(lnt,rnt+1);
now-=1ll*a[pos]*(pos-lnt+1)+fl[rnt+1]-fl[pos];
}
void rmr(){
int pos=findit(lnt,rnt+1);
now+=1ll*a[pos]*(pos-lnt+1)+fl[rnt+1]-fl[pos];
rnt++;
}
int main()
{
a[0]=0x7f7f7f7f;
scanf("%d%d",&n,&m);
for(register int i=1;i<=n;i++)
a[i]=readit();
int p=2,t=0;
for(register int i=1;i<=n;i++){
if(i>p) p*=2,t++;
lg[i]=t;
st[i][0]=i;
}
for(register int i=1;i<=16;i++)
for(register int j=1;j<=n;j++)
{
if(a[st[j][i-1]]>a[st[j+(1<<(i-1))][i-1]])
st[j][i]=st[j+(1<<(i-1))][i-1];
else st[j][i]=st[j][i-1];
}
for(register int i=1;i<=n;i++){
while(!d.empty()&&d.top().x>a[i]) d.pop();
if(!d.empty()) l[i]=d.top().id;
yyy.id=i;yyy.x=a[i];
d.push(yyy);
}
while(!d.empty()) d.pop();
for(register int i=n;i>=1;i--){
while(!d.empty()&&d.top().x>a[i]) d.pop();
if(!d.empty()) r[i]=d.top().id;
yyy.id=i;yyy.x=a[i];
d.push(yyy);
}
for(register int i=1;i<=m;i++)
scanf("%d%d",&b[i].l,&b[i].r),b[i].tim=i;
sort(b+1,b+1+m);
for(register int i=1;i<=n;i++){
if(l[i]==0) fl[i]=1ll*i*a[i];
else fl[i]=fl[l[i]]+1ll*(i-l[i])*a[i];
}
for(register int i=n;i>=1;i--){
if(r[i]==0) fr[i]=1ll*(n-i+1)*a[i];
else fr[i]=fr[r[i]]+1ll*(r[i]-i)*a[i];
}
lnt=1,rnt=1,now=a[1];
for(register int i=1;i<=m;i++){
while(rnt<b[i].r) rmr();
while(lnt>b[i].l) lml();
while(rnt>b[i].r) rml();
while(lnt<b[i].l) lmr();
ans[b[i].tim]=now;
}
for(register int i=1;i<=m;i++)
printf("%lld\n",ans[i]);
}