[Hnoi2016]序列(莫队+st表)

4540: [Hnoi2016]序列

Description

  给定长度为 n 的序列: a 1 , a 2 ,…, a n ,记为 a [ 1 : n ] 。类似地, a [ l : r ] 1 l r N )是指序列: a l , a l + 1 ,…, a r 1 , a r 。若 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 个整数为 a i ,即序列第 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 , | A i | 10 9












解:

听说可以大力上线段树?可是我不会啊。
其实莫队也不怎么会。
还是说说怎么做吧。首先莫队的方法可以把这个问题变成对于区间[l,r],以r为右端点,再求答案。而且由于莫队的复杂度,我们需要做到 O ( 1 )
怎么求答案?本来想的是存一个每个点左右比它小的第一个数,结果发现似乎变成了一个递归的子问题,感觉还是很无解。
然后在网上看到一种很巧妙的做法。我们现在有区间[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]);
}

猜你喜欢

转载自blog.csdn.net/lvmaooi/article/details/80739668