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 2 4 1 3
1 5
1 3
2 4
3 5
2 5
Sample Output
17
11
11
17
HINT
1 ≤N,Q ≤ 100000,|Ai| ≤ 10^9
正解:莫队算法+$ST$表+单调栈。
一个只用过一次的数据结构,一个从没用过的数据结构。。
首先,这道题允许离线,于是我们想到莫队算法。
我们考虑移动右端点$r$,能产生多少贡献。当我们移动端点以后,我们能够产生的贡献就是$(r-last+1)*a[r]$,其中$last$为$r$左边比它小的第一个元素。那么我们可以用单调栈$O(n)$地预处理出左边比它小的第一个数的位置和右边比它小的第一个数的位置。然后我们在查询时,可以一路往左边跳,并计算贡献,直到跳到这个区间的最后一个数,而这个数肯定是这个区间最小的数。那么我们可以考虑用$ST$表,记录一个区间中最小数的出现位置。同时,我们记录一个类似于前缀和的东西,记录出贡献的前缀和,即$suml[r]=suml[last[r]]+(r-last[r])*a[r]$。然后我们就可以$O(1)$地查询了。同样,移动左端点与移动右端点类似。
话说这道题还是我的$ST$表入门题呢。。
1 //It is made by wfj_2048~ 2 #include <algorithm> 3 #include <iostream> 4 #include <complex> 5 #include <cstring> 6 #include <cstdlib> 7 #include <cstdio> 8 #include <vector> 9 #include <cmath> 10 #include <queue> 11 #include <stack> 12 #include <map> 13 #include <set> 14 #define inf (1<<30) 15 #define N (500010) 16 #define il inline 17 #define RG register 18 #define ll long long 19 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) 20 21 using namespace std; 22 23 struct data{ ll i,l,r; }q[N]; 24 25 ll a[N],st[N],lg[N],bl[N],lst[N],nxt[N],suml[N],sumr[N],ans[N],ST[19][N],n,Q,L,R,Ans,top,block; 26 27 il ll gi(){ 28 RG ll x=0,q=1; RG char ch=getchar(); 29 while ((ch<'0' || ch>'9') && ch!='-') ch=getchar(); 30 if (ch=='-') q=-1,ch=getchar(); 31 while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); 32 return q*x; 33 } 34 35 il ll cmp(const data &a,const data &b){ 36 if (bl[a.l]==bl[b.l]) return a.r<b.r; 37 return bl[a.l]<bl[b.l]; 38 } 39 40 il void buildST(){ 41 for (RG ll i=2;i<=n;++i) lg[i]=lg[i>>1]+1; 42 for (RG ll j=1;j<=17;++j) 43 for (RG ll i=1;i<=n;++i){ 44 RG ll t=i+(1<<(j-1)); 45 ST[j][i]=a[ST[j-1][i]]<=a[ST[j-1][t]] ? ST[j-1][i] : ST[j-1][t]; 46 } 47 return; 48 } 49 50 il ll query(RG ll l,RG ll r){ 51 RG ll t=lg[r-l+1],c=r-(1<<t)+1; 52 return a[ST[t][l]]<=a[ST[t][c]] ? ST[t][l] : ST[t][c]; 53 } 54 55 il void updatel(RG ll l,RG ll r,RG ll fg){ 56 RG ll x=query(l,r),res=sumr[l]-sumr[x]+(r-x+1)*a[x]; 57 Ans+=fg*res; return; 58 } 59 60 il void updater(RG ll l,RG ll r,RG ll fg){ 61 RG ll x=query(l,r),res=suml[r]-suml[x]+(x-l+1)*a[x]; 62 Ans+=fg*res; return; 63 } 64 65 il void work(){ 66 n=gi(),Q=gi(),block=sqrt(n),st[top=1]=1; 67 for (RG ll i=1;i<=n;++i) a[i]=gi(),ST[0][i]=i,bl[i]=(i-1)/block+1; 68 for (RG ll i=2;i<=n;++i){ 69 while (top && a[st[top]]>=a[i]) nxt[st[top--]]=i; 70 lst[i]=st[top],st[++top]=i; 71 } 72 while (top) nxt[st[top--]]=n+1; 73 for (RG ll i=1;i<=n;++i) suml[i]=suml[lst[i]]+(i-lst[i])*a[i]; 74 for (RG ll i=n;i;--i) sumr[i]=sumr[nxt[i]]+(nxt[i]-i)*a[i]; 75 for (RG ll i=1;i<=Q;++i) q[i].i=i,q[i].l=gi(),q[i].r=gi(); 76 buildST(),sort(q+1,q+Q+1,cmp),L=1,R=0,Ans=0; 77 for (RG ll i=1;i<=Q;++i){ 78 while (L>q[i].l) L--,updatel(L,R,1); 79 while (R<q[i].r) R++,updater(L,R,1); 80 while (L<q[i].l) updatel(L,R,-1),L++; 81 while (R>q[i].r) updater(L,R,-1),R--; 82 ans[q[i].i]=Ans; 83 } 84 for (RG ll i=1;i<=Q;++i) printf("%lld\n",ans[i]); return; 85 } 86 87 int main(){ 88 File("sequence"); 89 work(); 90 return 0; 91 }