[HNOI2016]序列
Tea神题解 Kelin神题解
对于莫队算法最主要的是如何快速算出[l,r]→[l,r+1][l,r]\to[l,r+1][l,r]→[l,r+1]对答案的贡献的变化。
当询问区间发生上述变化时[l,r]→[l,r+1][l,r]\to [l,r+1][l,r]→[l,r+1]不难发现子区间增加这些:[l,r+1],[l+1,r+1],…,[r,r+1],[r+1,r+1][l,r+1],[l+1,r+1],\dots,[r,r+1],[r+1,r+1][l,r+1],[l+1,r+1],…,[r,r+1],[r+1,r+1]总共r−l+2r-l+2r−l+2个子区间[l→r+1,r+1][l\to r+1,r+1][l→r+1,r+1],需要求出他们对答案的贡献即每个区间的区间最小值之和。
考虑求出[l,r+1][l,r+1][l,r+1]区间最小值的位置是ppp,不难得知这些子区间[l→p,r+1][l\to p,r+1][l→p,r+1]的最小值都是apa_pap,这部分对答案的贡献是ap×(p−l+1)a_p×(p-l+1)ap×(p−l+1)这里可以用ST表预处理快速求出。
而对于这些子区间[p+1→r+1,r+1][p+1\to r+1,r+1][p+1→r+1,r+1]
预处理fif_ifi:表示以iii为区间右端点的最小值之和
fi=fLi+ai×(i−Li)f_i=f_{L_i}+a_i×(i-L_i)fi=fLi+ai×(i−Li)
LiL_iLi表示iii左边第一个小于等于aia_iai的位置。(可以用单调栈预处理)
不难发现一定有xxx使得Lx=pL_x=pLx=p于是有fr+1=ar+1×(r+1−Lr+1)+⋯+ax×(x−p)+fpf_{r+1}=a_{r+1}×(r+1-L_{r+1})+\dots+a_x×(x-p)+f_pfr+1=ar+1×(r+1−Lr+1)+⋯+ax×(x−p)+fp
于是子区间[p+1→r+1,r+1][p+1\to r+1,r+1][p+1→r+1,r+1]对答案的贡献是fr+1−fpf_{r+1}-f_pfr+1−fp
对于[l,r]→[l−1,r][l,r]\to[l-1,r][l,r]→[l−1,r]可以效仿移动右端点的方式预处理RiR_iRi以及gig_igi即可。
时间复杂度O(nlogn+nn)O(n\log n+n\sqrt{n})O(nlogn+nn)
注意莫队首先移动右端点因为初始化时,l=1,r=0l=1,r=0l=1,r=0
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
using pii=pair<int,int>;
using ll=long long;
constexpr int N=100010;
pii ST[N][22];
int lg[N];
int a[N],n,m;
int L[N],R[N],stk[N],top;
ll f[N],g[N];
int sz,pos[N];
void init(int n)
{lg[1]=0;for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;for(int i=1;i<=n;i++) ST[i][0]={a[i],i};for(int k=1;k<=lg[n];k++)for(int i=1;i+(1<<k)-1<=n;i++) ST[i][k]=min(ST[i][k-1],ST[i+(1<<k-1)][k-1]);top=0;for(int i=1;i<=n;i++){while(top&&a[stk[top]]>a[i]) top--;L[i]=stk[top];stk[++top]=i;}stk[top=0]=n+1;for(int i=n;i>=1;i--){while(top&&a[stk[top]]>a[i]) top--;R[i]=stk[top];stk[++top]=i;}for(int i=1;i<=n;i++) f[i]=f[L[i]]+1ll*(i-L[i])*a[i];for(int i=n;i>=1;i--)g[i]=g[R[i]]+1ll*(R[i]-i)*a[i];sz=sqrt(n);for(int i=1;i<=n;i++) pos[i]=i/sz;}
int query(int l,int r)// [l,r]最小值的位置
{int k=lg[r-l+1];return min(ST[l][k],ST[r-(1<<k)+1][k]).second;
}
struct node
{int l,r;int id;bool operator<(const node&o)const{return pos[l]<pos[o.l]||pos[l]==pos[o.l]&&r<o.r;}
}q[N];
ll calcR(int l,int r)
{int p=query(l,r);return 1ll*a[p]*(p-l+1)+f[r]-f[p];
}
ll calcL(int l,int r)
{int p=query(l,r);return 1ll*a[p]*(r-p+1)+g[l]-g[p];
}
ll ans[N],res;
int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);cin>>n>>m;for(int i=1;i<=n;i++) cin>>a[i];init(n);for(int i=1;i<=m;i++){int l,r;cin>>l>>r;q[i]={l,r,i};}sort(q+1,q+1+m);int l=1,r=0;for(int i=1;i<=m;i++){while(r<q[i].r) res+=calcR(l,++r);//首先考虑右端点while(r>q[i].r) res-=calcR(l,r--);while(l<q[i].l) res-=calcL(l++,r);while(l>q[i].l) res+=calcL(--l,r);ans[q[i].id]=res;}for(int i=1;i<=m;i++) cout<<ans[i]<<'\n';return 0;
}
注意我改了2小时,对比代码才改出来,无语了~~~
要加油哦~