bzoj4540: [Hnoi2016]序列

来源:互联网 发布:deepin debian ubuntu 编辑:程序博客网 时间:2024/05/10 18:17

题目链接

bzoj4540

题目描述

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

题解

这里提供一种nn 的做法。
我们考虑使用莫队算法,当左右端点移动时,只会增加或删除以某个元素开头或结尾的子串。这里我们考虑右端点右移至r。那么新增的子串为s[i...r]l<=i<=r。我们考虑lr中的最小值p,假设他的位置是k,则它对答案的贡献为(kl+1)p。我们记ls[i]表示i的左边第一个比i小的元素的位置。然后我们发现第r个元素对答案的贡献是(ils[i])a[i]ils[i]+1中的元素对答案没有贡献。第ls[i]个元素对答案的贡献是(ls[i]ls[ls[i]])a[ls[i]],依次列推直到p。这样我们可以预处理一个类似前缀和的数组,然后通过rmq可以求出p,这样就可以O(1) 实现转移了。


#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<cmath>#include<queue>#include<vector>using namespace std;const int N=100010;int stk[N],a[N],rmq[N][20],r[N],l[N],Log[N],top,S,n,m,x,y;long long ans[N],dl[N],dr[N],now;struct query{    int l,r,id;    friend bool operator <(const query &a,const query &b){        if(a.l/S==b.l/S) return a.r<b.r;        return a.l/S<b.l/S;    }}q[N];inline void Read(int &x){    static char c;    int f=1;    for(c=getchar();c<'0'||c>'9';c=getchar()) if(c=='-') f=-1;    for(x=0;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';    x=x*f;}int query(int l,int r){    int tmp=Log[r-l+1];    if(a[rmq[l][tmp]]<a[rmq[r-(1<<tmp)+1][tmp]])    return rmq[l][tmp];    else return rmq[r-(1<<tmp)+1][tmp];}void changel(int l,int r,long long k){    int tmp=query(l,r);    now+=k*a[tmp]*(r-tmp+1);    now+=k*(dr[l]-dr[tmp]);}void changer(int l,int r,long long k){    int tmp=query(l,r);    now+=k*a[tmp]*(tmp-l+1);    now+=k*(dl[r]-dl[tmp]);}void pretreat(){    for(int i=1;i<=n;i++) Log[i]=(int)log2(i);    for(int i=1;i<=n;i++){        while(top&&a[stk[top]]>a[i]) r[stk[top--]]=i;        l[i]=a[stk[top]]==a[i]?l[stk[top]]:stk[top];        stk[++top]=i;    }    while(top) r[stk[top--]]=n+1;    for(int i=1;i<=n;i++) dl[i]=dl[l[i]]+1ll*(i-l[i])*a[i];    for(int i=n;i;i--) dr[i]=dr[r[i]]+1ll*(r[i]-i)*a[i];    for(int i=1;i<=n;i++) rmq[i][0]=i;    for(int i=1;(1<<i)<=n;i++)     for(int j=1;j<=n-(1<<i)+1;j++)     if(a[rmq[j][i-1]]<a[rmq[j+(1<<(i-1))][i-1]])     rmq[j][i]=rmq[j][i-1];      else rmq[j][i]=rmq[j+(1<<(i-1))][i-1];}int main(){    Read(n); Read(m);    S=(int)sqrt(n);    for(int i=1;i<=n;i++) Read(a[i]);    pretreat();    for(int i=1;i<=m;i++){        Read(q[i].l); Read(q[i].r);        q[i].id=i;    }    sort(q+1,q+1+m);    x=y=1; now=a[1];    for(int i=1;i<=m;i++){        if(q[i].r>y) while(y!=q[i].r) y++,changer(x,y,1);        if(q[i].l>x) while(x!=q[i].l) changel(x,y,-1),x++;        if(q[i].l<x) while(x!=q[i].l) x--,changel(x,y,1);        if(q[i].r<y) while(y!=q[i].r) changer(x,y,-1),y--;        ans[q[i].id]=now;    }    for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);    return 0;}
1 0
原创粉丝点击