二次离线与值域分块
EnofTaiPeople · · 题解
二次离线莫队,是一种可以做到
一般需要一个数字对于一个区间的贡献具有结合律且问题不强制在线。
设
我们只需要计算
差分一下:
发现另一部分的查询次数是
总时间复杂度:
开 O2 只需 1.65s,真的不卡常!
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=5e5+5,C=2e5+5,K=305;
inline void File(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
}
namespace fast_io{
char ib[N+5],ob[N+85],stk[85];
int it,ed,c,ot,t;
#define _gc (it==ed&&(ed=(it=0)+fread(ib,1,N,stdin),it==ed))?EOF:ib[it++]
inline void read(int &x){
for(c=_gc;c<48;c=_gc);
for(x=0;c>47;c=_gc)x=x*10+(48^c);
}
inline void flush(){
fwrite(ob,1,ot,stdout),ot=0;
}
inline void write(ll x,char c='\n'){
t=0;while(x)stk[++t]=48^(x%10),x/=10;
while(t)ob[ot++]=stk[t--];
ob[ot++]=c;if(ot>N)flush();
}
};using fast_io::read;
using fast_io::write;
int n,m,ql[N],qr[N],qt;
int a[N],ct[C],Ct[K],mp[C],mt,p[N];
ll ans[N],fc[N],sm[C],Sm[C];
struct adt{int l,r,id,tp;};
vector<adt>G[N];
inline void add(int x){
int i,d=a[x]+1;
for(i=d;(i>>9)==(d>>9);++i)++ct[i];
for(i=(d>>9)+1;i<=256;++i)++Ct[i];d-=2;
for(i=d;(i>>9)==(d>>9);--i)sm[i]+=a[x];
for(i=(d>>9)-1;i>=0;--i)Sm[i]+=a[x];
}
inline ll get(int d){
return Sm[d>>9]+sm[d]+ll(Ct[d>>9]+ct[d])*d;
}
signed main(){
File();
read(n),read(m),qt=1+n/(1+sqrt(m));
int i,j,L,l,R,r;
for(i=1;i<=n;++i)
read(a[i]),fc[i]=get(a[i]),add(i);
memset(Sm,0,sizeof(Sm));
memset(Ct,0,sizeof(Ct));
memset(sm,0,sizeof(sm));
memset(ct,0,sizeof(ct));
for(i=1;i<=m;++i)
read(ql[i]),read(qr[i]),p[i]=i;
stable_sort(p+1,p+m+1,[&](int x,int y){
return (ql[x]/qt==ql[y]/qt)?((ql[x]/qt)&1)?
qr[x]<qr[y]:qr[x]>qr[y]:ql[x]<ql[y];
});
l=1,r=0;
for(i=1;i<=m;++i){
L=ql[p[i]],R=qr[p[i]];
if(l>L){
if(r)G[r].push_back({L,l-1,p[i],1});
while(l>L)--l,ans[p[i]]-=fc[l]-a[l];
}if(r<R){
if(l>1)G[l-1].push_back({r+1,R,p[i],-1});
while(r<R)++r,ans[p[i]]+=fc[r]+a[r];
}
// printf("l:%d r:%d ans[i]:%d\n",l,r,ans[i]);
if(l<L){
if(r)G[r].push_back({l,L-1,p[i],-1});
while(l<L)ans[p[i]]+=fc[l]-a[l],++l;
}if(r>R){
if(l>1)G[l-1].push_back({R+1,r,p[i],1});
while(r>R)ans[p[i]]-=fc[r]+a[r],--r;
}
// printf("l:%d r:%d ans[i]:%d\n",l,r,ans[i]);
// puts("");
}
for(i=1;i<=n;++i){
add(i);
// printf("get2:%d\n",get(2));
for(adt at:G[i])
for(j=at.l;j<=at.r;++j)
ans[at.id]+=get(a[j])*at.tp;
}
// for(i=1;i<=m;++i)printf("%d ",ans[i]);puts("");
for(i=1;i<=m;++i)ans[p[i]]+=ans[p[i-1]];
for(i=1;i<=m;++i)printf("%lld\n",ans[i]);
// write(ans[i]);
fast_io::flush();return 0;
}