bzoj4540: [Hnoi2016]序列
Posted thy_asdf
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj4540: [Hnoi2016]序列相关的知识,希望对你有一定的参考价值。
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4540
思路:又是莫队....
我们发现左右端点移动时,只会增加或删除某个点开头或结尾的区间
先考虑右端点从r移动到r+1
令p为[l,r]中最小值的位置
那么它会对新加的区间中的p-l+1个区间产生a[p]的贡献
另一些左端点在[p+1,r],右端点是r+1的区间怎么统计呢?
首先用单调栈求出l[i],r[i]表示向左第一个小于a[i]的数的位置,向右第一个小于等于a[i]的数的位置
(注意:一个是小于,一个是小于等于,这样是防止重复计算)
预处理出两个类似前缀和的数组sl[i]和sr[i]
其中sl[i]=sl[l[i]]+(i-l[i])*a[i],sr同理
表示以这个为结束的答案的前缀和
左端点在l[i]及以前的答案就是sl[l[i]],左端点在[l[i],i]之间的最小值肯定是a[i],所以答案加上(i-l[i])*a[i]
那么左端点在[p+1,r],右端点是r+1的区间对答案的贡献就是sl[r+1]-sl[p]
那么移动后,答案加上(p-l+1)*a[i]+sl[r]-sl[p]即可
左端点移动及删除类似
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
typedef long long ll;
const int maxn=100010,maxk=22,inf=1e9;
using namespace std;
int n,Q,sz,top,a[maxn],st[maxn][maxk],pw[maxk],stk[maxn],L[maxn],R[maxn],bel[maxn],lg[maxn];ll sl[maxn],sr[maxn],now,ans[maxn];
struct querint l,r,id;q[maxn];
bool operator <(quer a,quer b)return bel[a.l]==bel[b.l]?a.r<b.r:bel[a.l]<bel[b.l];
int getmin(int x,int y)return a[x]<a[y]?x:y;
int calc(int x,int y)
if (x>y) swap(x,y);
int l=lg[y-x+1];
//printf("x=%d y=%d log=%d minpos%d\\n",x,y,l,getmin(st[x][l],st[y-pw[l]+1][l]));
return getmin(st[x][l],st[y-pw[l]+1][l]);
void modifyl(int l,int r,int op)
int p=calc(l,r);
ll tmp=1ll*a[p]*(r-p+1)+sr[l]-sr[p];
now+=op*tmp;
void modifyr(int l,int r,int op)
int p=calc(l,r);
ll tmp=1ll*a[p]*(p-l+1)+sl[r]-sl[p];
now+=op*tmp;
void work()
sort(q+1,q+1+Q);now=a[1];
for (int l=1,r=1,i=1;i<=Q;i++)
//printf("i=%d\\n",i);
for (;r<q[i].r;r++) modifyr(l,r+1,1);
for (;l>q[i].l;l--) modifyl(l-1,r,1);
for (;r>q[i].r;r--) modifyr(l,r,-1);
for (;l<q[i].l;l++) modifyl(l,r,-1);
ans[q[i].id]=now;
for (int i=1;i<=Q;i++) printf("%lld\\n",ans[i]);
int main()
//freopen("sequence1.in","r",stdin);freopen("sequence.out","w",stdout);
scanf("%d%d",&n,&Q),sz=sqrt(n),lg[0]=-1;
pw[0]=1;for (int i=1;i<=18;i++) pw[i]=pw[i-1]<<1;
for (int i=1;i<=n;i++) lg[i]=lg[i>>1]+1,bel[i]=(i-1)/sz+1;
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=1;i<=n;i++) st[i][0]=i;
for (int j=1;j<=18;j++)
for (int i=1;i<=n;i++)
st[i][j]=st[i][j-1];
if (i+pw[j-1]<=n) st[i][j]=getmin(st[i][j-1],st[i+pw[j-1]][j-1]);
//printf("%d ",st[i][j]);
for (int i=1;i<=n;i++)
while (top&&a[stk[top]]>=a[i]) R[stk[top--]]=i;
stk[++top]=i;
while (top) R[stk[top--]]=n+1;
for (int i=n;i;i--)
while (top&&a[stk[top]]>a[i]) L[stk[top--]]=i;
stk[++top]=i;
while (top) L[stk[top--]]=0;
//for (int i=1;i<=n;i++) printf("i=%d L=%d R=%d\\n",i,L[i],R[i]);
for (int i=n;i;i--) sr[i]=sr[R[i]]+1ll*(R[i]-i)*a[i];
for (int i=1;i<=n;i++) sl[i]=sl[L[i]]+1ll*(i-L[i])*a[i];
//for (int i=1;i<=n;i++) printf("i=%d sl=%lld sr=%lld\\n",i,sl[i],sr[i]);
for (int i=1;i<=Q;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
work();
return 0;
/*
5 5
5 2 4 1 3
1 5
1 3
2 4
3 5
2 5
*/
以上是关于bzoj4540: [Hnoi2016]序列的主要内容,如果未能解决你的问题,请参考以下文章