bzoj 4540: [Hnoi2016]序列

题意坑爹,不需要判断两个子串是否相等。
然后这道题几乎就是bzoj 4262了。
除了线段树做法,还可以用莫队
用单调队列预处理出L[i]表示i左边第一个小于a[i]的位置,R[i]表示i右边第一个小于a[i]的位置。
fl[i]表示以i为左端点的所有区间的答案,=a[i]*(R[i]-i)+fl[R[i]],因为[i,R[i])位置的贡献都是a[i],[R[i],n]这个区间的答案与fr[R[i]]相同。
fr[i]同理。
跑莫队,区间[l,r],其中l是待加入的数,令p表示这个区间中最小的数的位置,那么对答案的贡献为a[p]*(r-p+1)+fl[x]-fl[p]。
这个做法的核心是发现区间答案的可减性,即如果存在y>x且a[y]<a[x],那么对于z>=y,区间[x,z]的最小值=区间[y,z]最小值。
    
    
    
    
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define inf 1e9
#define eps 1e-8
#define md
#define N 100010
using namespace std;
struct QQ { int l,r,id;} qu[N];
int st[N][20],a[N],q[N],L[N],R[N],pos[N];
ll fl[N],fr[N],ans[N],sum;
int n;
 
int get(int x,int y) { if (a[x]<a[y]) return x; else return y;}
 
void build_ST()
{
for (int i=1;i<=n;i++) st[i][0]=i;
for (int j=1;j<=18;j++)
for (int i=1;i+(1<<(j-1))<=n;i++)
st[i][j]=get(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
 
void build_lr()
{
int top=0;
for (int i=1;i<=n;i++)
{
while (top&&a[q[top]]>a[i]) { R[q[top]]=i; top--;}
q[++top]=i;
}
for (int i=top;i;i--) R[q[i]]=n+1;
top=0;
for (int i=n;i;i--)
{
while (top&&a[q[top]]>a[i]) { L[q[top]]=i; top--;}
q[++top]=i;
}
for (int i=1;i<=n;i++) fr[i]=1ll*a[i]*(i-L[i])+fr[L[i]];
for (int i=n;i;i--) fl[i]=1ll*a[i]*(R[i]-i)+fl[R[i]];
int K=sqrt(n); for (int i=1;i<=n;i++) pos[i]=(i-1)/K+1;
}
 
bool cmp(QQ a,QQ b) { return pos[a.l]==pos[b.l]?a.r<b.r:a.l<b.l;}
 
void add_l(int l,int r,int ty)
{
int j=log2(r-l+1+eps),p=get(st[l][j],st[r-(1<<j)+1][j]);
sum+=ty*(fl[l]-fl[p]+1ll*a[p]*(r-p+1));
}
 
void add_r(int l,int r,int ty)
{
int j=log2(r-l+1+eps),p=get(st[l][j],st[r-(1<<j)+1][j]);
sum+=ty*(fr[r]-fr[p]+1ll*a[p]*(p-l+1));
}
 
int main()
{
int m;
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
build_ST();
build_lr();
for (int i=1;i<=m;i++) { scanf("%d%d",&qu[i].l,&qu[i].r); qu[i].id=i;}
sort(qu+1,qu+m+1,cmp);
int l=1,r=0;
for (int i=1;i<=m;i++)
{
while (qu[i].l<l) { l--; add_l(l,r,1);}
while (r<qu[i].r) { r++; add_r(l,r,1);}
while (l<qu[i].l) { add_l(l,r,-1); l++;}
while (qu[i].r<r) { add_r(l,r,-1); r--;}
ans[qu[i].id]=sum;
}
for (int i=1;i<=m;i++) printf("%lld\n",ans[i]);
return 0;
}


你可能感兴趣的:(bzoj 4540: [Hnoi2016]序列)