BZOJ4540: [Hnoi2016]序列
4540: [Hnoi2016]序列
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 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
5 2 4 1 3
1 5
1 3
2 4
3 5
2 5
Sample Output
28
17
11
11
17
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; }