luogu P5501 [LnOI2019]来者不拒,去者不追
题面传送门
话说我居然连快读都没加就过了。
容易想到莫队。
首先可以看出一个节点增/删有两个贡献:区间比它小的数的个数和区间比它大的数的和。
容易想到可以用树状数组维护,但是这个是\(O(n\sqrt nlogw)\)的,不是很优秀。
考虑莫队二次离线,那么分开算贡献即可。
注意离线时用域值分块可以做到\(O(n\sqrt n)\)
注意这道题还要离散,离散后算贡献别忘了离散回去。
代码实现:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
int n,m,k,x,y,z,a[500039],l,r,mid,maxn,nows[500039],tots[500039],ys[500039];
long long fs[339],f[100039],ans[500039],tot[500039],f1[500039],f2[500039],q[500039];
struct yyy{int x,y,id;}s[500039],tmp;
inline bool cmp(yyy x,yyy y){return x.x/k==y.x/k?(((x.x/k)&1)?x.y<y.y:x.y>y.y):x.x<y.x;}
struct ques{int x,y,flag,id;}tmps;
vector<ques> g[500039];
inline void get1(int x){
register int i;
for(i=x+1;i<=x/k*k+k-1;i++) f[i]++;
for(i=x/k+1;i<=maxn/k;i++) fs[i]++;
}
inline void get2(int x){
register int i;
for(i=0;i<x/k;i++) fs[i]+=ys[x];
for(i=x/k*k;i<x;i++) f[i]+=ys[x];
}
inline void ls(int &x){for(l=0,r=n;l+1<r;)mid=l+r>>1,(x>nows[mid])?(l=mid):(r=mid);x=tots[r];}
int main(){
freopen("1.in","r",stdin);
register int i,j,h;
scanf("%d%d",&n,&m);k=sqrt(n);
for(i=1;i<=n;i++) scanf("%d",&a[i]),q[i]=q[i-1]+a[i],nows[i]=a[i];
sort(nows+1,nows+n+1);
for(i=1;i<=n;i++) tots[i]=((i^1)?(tots[i-1]+(nows[i]!=nows[i-1])):1),ys[tots[i]]=nows[i];
for(i=1;i<=n;i++) ls(a[i]),maxn=max(maxn,a[i]);
for(i=1;i<=m;i++)scanf("%d%d",&x,&y),s[i]=(yyy){x,y,i};
sort(s+1,s+m+1,cmp);
for(i=1;i<=n;i++) get1(a[i]),f1[i]=fs[a[i]/k]+f[a[i]];
for(memset(f,0,sizeof(f)),memset(fs,0,sizeof(fs)),i=1;i<=n;i++)get2(a[i]),f2[i]=fs[a[i]/k]+f[a[i]];
for(l=1,r=0,i=1;i<=m;i++){
tmp=s[i];
if(r<tmp.y){
g[l-1].push_back((ques){r+1,tmp.y,-1,i});
while(r<tmp.y)ans[i]+=f1[++r]*ys[a[r]]+f2[r];
}
if(r>tmp.y){
g[l-1].push_back((ques){tmp.y+1,r,1,i});
while(r>tmp.y)ans[i]-=f1[r]*ys[a[r]]+f2[r--];
}
if(l>tmp.x){
g[r].push_back((ques){tmp.x,l-1,1,i});
while(l>tmp.x)ans[i]-=f1[--l]*ys[a[l]]+f2[l];
}
if(l<tmp.x){
g[r].push_back((ques){l,tmp.x-1,-1,i});
while(l<tmp.x)ans[i]+=f1[l]*ys[a[l]]+f2[l++];
}
}
for(memset(f,0,sizeof(f)),memset(fs,0,sizeof(fs)),i=1;i<=n;i++){
get1(a[i]);
for(j=0;j<g[i].size();j++){
tmps=g[i][j];
for(h=tmps.x;h<=tmps.y;h++)
ans[tmps.id]+=(fs[a[h]/k]+f[a[h]])*ys[a[h]]*tmps.flag;
}
}
for(memset(f,0,sizeof(f)),memset(fs,0,sizeof(fs)),i=1;i<=n;i++){
get2(a[i]);
for(j=0;j<g[i].size();j++){
tmps=g[i][j];
for(h=tmps.x;h<=tmps.y;h++) ans[tmps.id]+=(fs[a[h]/k]+f[a[h]])*tmps.flag;
}
}
for(i=1;i<=m;i++) ans[i]+=ans[i-1],tot[s[i].id]=ans[i]+q[s[i].y]-q[s[i].x-1];
for(i=1;i<=m;i++) printf("%lld\n",tot[i]);
}