P5501 [LnOI2019]来者不拒,去者不追 题解
首先进行一个转化:每个询问区间 \([l,r]\) 中一对 \((i,j)\),如果 \(a_i< a_j\) 会造成 \(a_i\) 的贡献。最后再加一个区间和就是答案。
考虑这是一个区间每对数的贡献,并且具有可减性,上莫队二次离线那一套。考虑一个位置 \(x\) 对一个区间 \([l,r]\) 的贡献 \(f(x,[l,r])\),拆成两部分:
-
\([l,r]\) 中 \(< a_x\) 的数的个数为 \(c\),贡献 \(c\times a_x\)
-
\([l,r]\) 中 \(> a_x\) 的数的和 \(s\)
两部分都是可减信息,由于莫队二次离线是 \(O(n)\) 次单点修改,\(O(n\sqrt m)\) 次单点查询,所以需要平衡一下复杂度,在值域上分块可以做到 \(O(\sqrt V)\) 修改,\(O(1)\) 查询(其中 \(V\) 是值域)。总复杂度 \(O(n\sqrt V+n\sqrt m+m)\)。
点击查看代码
#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#define memset __builtin_memset
#define pb push_back
typedef long long ll;
inline void chkmax(int &a,const int &b){if(b>a)a=b;}
const int N=5e5+13,M=5e5+13,B=1000+13,BN=400,BM=350;
int n,m,lim,tot,a[N],fromq[N],from[M],L[B],R[B],c1[M],tag1[B];
ll pre[N],c2[M],tag2[B],f[N];
struct Ques{
int l,r,id;ll sum;
inline bool operator <(const Ques &a)const{return fromq[l]!=fromq[a.l]?fromq[l]<fromq[a.l]:(fromq[l]&1?r<a.r:r>a.r);}
}q[N];
struct Node{int l,r,id,op;};
std::vector<Node> b[N];
ll ans[N];
int main(){
#ifdef winterfrost
freopen("pai.in","r",stdin);
freopen("P5501.out","w",stdout);
#endif
// return system("fc code1.out code2.out"),0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d",&a[i]),pre[i]=pre[i-1]+a[i],chkmax(lim,a[i]);
for(int i=1;i<=lim;++i){
from[i]=i/BM+1;
if(from[i]!=from[i-1]) R[tot]=i-1,L[++tot]=i;
}
R[tot]=lim;
for(int i=1;i<=n;++i){
f[i]=(ll)(c1[a[i]]+tag1[from[a[i]]])*a[i]+c2[a[i]]+tag2[from[a[i]]];
for(int j=a[i]+1;j<=R[from[a[i]]];++j) ++c1[j];
for(int j=from[a[i]]+1;j<=tot;++j) ++tag1[j];
for(int j=a[i]-1;j>=L[from[a[i]]];--j) c2[j]+=a[i];
for(int j=from[a[i]]-1;j>=1;--j) tag2[j]+=a[i];
}
for(int i=1;i<=m;++i) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
for(int i=1;i<=n;++i) fromq[i]=i/BN+1;
std::sort(q+1,q+m+1);
for(int i=1,l=1,r=0;i<=m;++i){
if(l>q[i].l) b[r].pb((Node){q[i].l,l-1,i,1});
while(l>q[i].l) --l,q[i].sum-=f[l];
if(r<q[i].r) b[l-1].pb((Node){r+1,q[i].r,i,-1});
while(r<q[i].r) ++r,q[i].sum+=f[r];
if(l<q[i].l) b[r].pb((Node){l,q[i].l-1,i,-1});
while(l<q[i].l) q[i].sum+=f[l],++l;
if(r>q[i].r) b[l-1].pb((Node){q[i].r+1,r,i,1});
while(r>q[i].r) q[i].sum-=f[r],--r;
}
memset(c1,0,sizeof c1);memset(c2,0,sizeof c2);memset(tag1,0,sizeof tag1);memset(tag2,0,sizeof tag2);
for(int i=1;i<=n;++i){
for(int j=a[i]+1;j<=R[from[a[i]]];++j) ++c1[j];
for(int j=from[a[i]]+1;j<=tot;++j) ++tag1[j];
for(int j=a[i]-1;j>=L[from[a[i]]];--j) c2[j]+=a[i];
for(int j=from[a[i]]-1;j>=1;--j) tag2[j]+=a[i];
for(auto x:b[i])
for(int j=x.l;j<=x.r;++j) q[x.id].sum+=x.op*((ll)(c1[a[j]]+tag1[from[a[j]]])*a[j]+c2[a[j]]+tag2[from[a[j]]]);
}
for(int i=1;i<=m;++i) ans[q[i].id]=(q[i].sum+=q[i-1].sum)+pre[q[i].r]-pre[q[i].l-1];
for(int i=1;i<=m;++i) printf("%lld\n",ans[i]);
return 0;
}