BZOJ4540: [Hnoi2016]序列

4540: [Hnoi2016]序列

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 1406  Solved: 659
[Submit][Status][Discuss]

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

思路{
  看到数据范围想到莫队。。。。。
  端点转移的话,基础的结论:对于一个当前区间$[l,r]$,新增端点$r+1$,产生新的$r-l+2$的区间,。
  这些某个端点固定,另外一个端点依次变化的一些区间,容易发现是这些区间的最小值在一段时间内连续不变。
  具体来说就是往那个方向所能延伸的最远长度,即$i$这个点到第一个小于它的数的位置。
  所以求出$L[i],R[i]$,$L[i]$为左边第一个小于它的数的位置,避免重复,$R[i]$为右边第一个小于等于它的数的位置。
  这个可以单调栈求出来。
  现在考虑端点移动。
  有了求的这个东西,我们发现当前区间$[l,r]$,新增端点$r+1$一定会向上述所说的那样算到$[l,r]$中值最小的位置。
  记录前缀和就可以快速求出新增端点到最小值的位置的贡献值.求这个位置倍增$ST$表
  设最小值位置$x$,$x$到另外一个端点$pos$,贡献$|x-pos|*a_x$.
  那么直接计算即可.
  有点卡常,把每个值的log,和$2^i$存起来,
}
#include<bits/stdc++.h>
#define il inline
#define RG register
#define ll long long
#define db double
#define N 200010
#define LL long long
using namespace std;
int n,m,a[N],L[N],R[N],st[N];
ll sl[N],sr[N],l,r,ans,Ans[N],bas[N];
int f[20][N],BL[N],Log[N];
struct Query{
  int l,r,id;
  void read(int xx){id=xx;scanf("%d%d",&l,&r);}
}q[N];
bool comp(const Query & a,const Query & b){
  return BL[a.l]==BL[b.l]?a.r<b.r:BL[a.l]<BL[b.l];
}
void init(){
  bas[0]=1;for(int i=1;i<=19;++i)bas[i]=bas[i-1]<<1;
  scanf("%d%d",&n,&m);
  int len=sqrt(n);
  for(RG int i=1;i<=n;++i){scanf("%d",&a[i]),BL[i]=(i-1)/len+1,f[0][i]=i;}
  for(RG int i=2;i<=n;++i)Log[i]=Log[i>>1]+1;
  for(int i=1;(1<<i)<=n;++i)
    for(int j=1;j+(1<<i)-1<=n;++j)
      f[i][j]=(a[f[i-1][j]]<a[f[i-1][j+bas[i-1]]])?f[i-1][j]:f[i-1][j+bas[i-1]];
  for(RG int i=1;i<=n;++i){
    while(st[0]&&a[st[st[0]]]>=a[i])R[st[st[0]--]]=i;
    st[++st[0]]=i;
  }
  while(st[0])R[st[st[0]--]]=n+1;
  for(RG int i=n;i;i--){
    while(st[0]&&a[st[st[0]]]>a[i])L[st[st[0]--]]=i;
    st[++st[0]]=i;
  }
  while(st[0])L[st[st[0]--]]=0;
  for(RG int i=1;i<=n;++i)sl[i]=sl[L[i]]+1ll*(i-L[i])*a[i];
  for(RG int i=n;i;i--)sr[i]=sr[R[i]]+1ll*(R[i]-i)*a[i];
  for(RG int i=1;i<=m;++i)q[i].read(i);
  sort(q+1,q+m+1,comp);
}
int cal(){
  int x=l,y=r;
  if(x>y)swap(x,y);
  int c=Log[y-x+1];
  return a[f[c][x]]<a[f[c][y-bas[c]+1]]?f[c][x]:f[c][y-bas[c]+1];
}
void Modifyl(int delta){
  int x=cal();
  int ls=l,rs=r;if(ls>rs)swap(ls,rs);
  ans+=delta*((sr[ls]-sr[x])+1ll*a[x]*(rs-x+1));
}
void Modifyr(int delta){
  int x=cal();
  int ls=l,rs=r;if(ls>rs)swap(ls,rs);
  ans+=delta*((sl[rs]-sl[x])+1ll*a[x]*(x-ls+1));
}
void work(){
  l=1,r=1,ans=a[1];
  for(int i=1;i<=m;++i){
    if(r<q[i].r)for(;r!=q[i].r;r++,Modifyr(1));
    if(l<q[i].l)for(;l!=q[i].l;Modifyl(-1),l++);
    if(l>q[i].l)for(;l!=q[i].l;l--,Modifyl(1));
    if(r>q[i].r)for(;r!=q[i].r;Modifyr(-1),r--);
    Ans[q[i].id]=ans;
  }
  for(int i=1;i<=m;++i)cout<<Ans[i]<<"\n";
}
int main(){
  init();
  work();
  return 0;
}

  

  

posted @ 2017-09-10 20:10  QYP_2002  阅读(246)  评论(0编辑  收藏  举报