分块,首先预处理所有整块之间的答案,这部分用类似莫队二离的手法可以改成 \(O(n)\) 次插入和 \(O(n\sqrt{n})\) 查询,然后根号平衡一手做到 \(O(n\sqrt{n})\);空间自然也是能线性的。当然更直白的说法是,直接预处理 \(f(i,j)\) 表示前 \(i\) 块中 \(>j\) 的元素个数。
然后考虑区间 \([l,r]\) 被分成 \(A_1,B,A_2\) 这样的左右散块和整块,那么我们已经处理好了 \(B\to B\),还剩下五块:\(A_1\to A_1/B/A_2,B\to A_2,A_2\to A_2\)。\(A_1\to B,B\to A_2\) 都可以利用 \(f\) 查询出来。
考虑 \(A_1\to A_1\),发现只需要从后往前扫,预处理 \(S_i\) 表示 \(i\) 同块内后面比他小的数的个数,那么只需要查 \(S\) 的区间和。同理 \(A_2\to A_2\) 也可以通过预处理 \(P_i\) 表示同块内前面比他大的数的个数来解决。
最后考虑 \(A_1\to A_2\),考虑提前把每块排序,然后查询的时候尝试归并这两块即可。
还需要考虑同块的情形。考虑差分成 \(P_r\) 减去 \(P_{l-1}\) 再减去 \([L,l-1]\to [l,r]\) 的贡献,其中 \(L\) 是这一块的左端点。那么这个贡献用这一块的排序后的数组也可以轻松算出。
卡到了最优解!
//-DYUNQIAN -std=c++14 -O2 -Wall
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read(){
ll x=0;char c=getchar();
for(;(c<'0'||c>'9');c=getchar());
for(;(c>='0'&&c<='9');c=getchar())x=x*10+(c&15);
return x;
}
const int mod=1e9+7;
int ksm(int x,int y,int p=mod){
int ans=1;
for(int i=y;i;i>>=1,x=1ll*x*x%p)if(i&1)ans=1ll*ans*x%p;
return ans%p;
}
int inv(int x,int p=mod){return ksm(x,p-2,p)%p;}
mt19937 rnd(time(0));
int randint(int l,int r){return rnd()%(r-l+1)+l;}
void Assert(bool c,int L=0){if(!c){cout<<"Assertion Failed at "<<L<<endl;exit(0);}}
const int N=1e5+5;
const int B=400;
const int NB=(int)(N/B);
vector<pair<int,int> >ov[NB+5];
#define fi first
#define se second
#define mk make_pair
int n,m,bl[N],st[NB+5],ed[NB+5],f[NB+5][N],pre[N],suf[N],a[N],C,S;
ll ans[NB+5][NB+5],fp[N],fs[N],g[NB+5][N];
struct BIT{
int c[N];
int lowbit(int x){return x&(-x);}
void add(int x,int v){for(int i=x;i<=n;i+=lowbit(i))c[i]+=v;}
int sum(int x,int res=0){for(int i=x;i;i-=lowbit(i))res+=c[i];return res;}
}T;
ll Q1(int l,int r){
if(l==st[bl[l]])return fp[r];
else if(r==ed[bl[r]])return fs[l];
int p=bl[l],cur=0;ll sum=fp[r]-fp[l-1];
for(auto t:ov[p]){
if(t.se<=l-1)cur++;
else if(t.se>=l&&t.se<=r)sum-=cur;
}
return sum;
}
int Q_leq(int l,int r,int v){// st[l] <= j <= ed[r] && a[j] < v
return f[r][v-1]-f[l-1][v-1];
}
int Q_geq(int l,int r,int v){return ed[r]-st[l]+1-f[r][v-1]+f[l-1][v-1];}
ll query(int l,int r){
if(bl[l]==bl[r])return Q1(l,r);
int pl=bl[l],pr=bl[r];
ll sum=fp[r]+fs[l]+ans[pl+1][pr-1];//A1 -> A1 , A2 -> A2 , B -> B
//A1 -> B & B -> A2
sum+=g[pr-1][ed[pl]]-g[pr-1][l-1],sum-=g[pl][ed[pl]]-g[pl][l-1];
sum-=g[pr-1][r]-g[pr-1][st[pr]-1],sum+=g[pl][r]-g[pl][st[pr]-1];
sum+=1ll*(ed[pr-1]-st[pl+1]+1)*(r-st[pr]+1);
//A1 -> A2
int L=ov[pl].size(),p=0,cur=0;
for(auto t:ov[pr]){
if(t.se>r)continue;
while(p<L&&ov[pl][p].fi>t.fi)cur+=(ov[pl][p].se>=l),p++;
sum+=cur;
}
return sum;
}
signed main(void){
#ifdef YUNQIAN
freopen("5046.in","r",stdin);
freopen("5046.out","w",stdout);
#endif
n=read(),m=read();S=B;
for(int i=1;i<=n;i++)a[i]=read();
memset(st,63,sizeof(st));
for(int i=1;i<=n;i++)bl[i]=(i-1)/S+1,st[bl[i]]=min(st[bl[i]],i),ed[bl[i]]=i;C=bl[n];
for(int i=1;i<=C;i++){
for(int j=st[i];j<=ed[i];j++)f[i][a[j]]++;
for(int j=1;j<=n;j++)f[i][j]+=f[i][j-1];
for(int j=1;j<=n;j++)f[i][j]+=f[i-1][j];
int now=0;
for(int j=st[i];j<=ed[i];j++)pre[j]=now-T.sum(a[j]),T.add(a[j],1),now++;
for(int j=st[i];j<=ed[i];j++)T.add(a[j],-1);now=0;
for(int j=ed[i];j>=st[i];j--)suf[j]=T.sum(a[j]),T.add(a[j],1);
for(int j=st[i];j<=ed[i];j++)T.add(a[j],-1);
for(int j=st[i]+1;j<=ed[i];j++)fp[j]=fp[j-1]+pre[j];
for(int j=ed[i]-1;j>=st[i];j--)fs[j]=fs[j+1]+suf[j];
for(int j=st[i];j<=ed[i];j++)ov[i].emplace_back(mk(a[j],j));
sort(ov[i].begin(),ov[i].end());reverse(ov[i].begin(),ov[i].end());
}
for(int i=1;i<=C;i++){
int cur=0;
for(int j=i;j<=C;j++){
ans[i][j]=ans[i][j-1];
for(int k=st[j];k<=ed[j];k++)ans[i][j]+=pre[k]+cur-f[j-1][a[k]]+f[i-1][a[k]];
cur+=ed[j]-st[j]+1;
}
}
//g[i][j] = sum{k in [1,j]} f[i][a[k]]
for(int i=1;i<=C;i++){
for(int j=1;j<=n;j++)g[i][j]=g[i][j-1]+f[i][a[j]];
}
// cout<<"prework Time = "<<(clock()-Start)/CLOCKS_PER_SEC<<endl;
ll lans=0;
for(int i=1;i<=m;i++){
// lans=0;
ll l=read(),r=read();l^=lans,r^=lans;
cout<<(lans=query(l,r))<<'\n';
}
return 0;
}
标签:pr,Ynoi2019,int,sum,sqrt,st,loves,NB,pl
From: https://www.cnblogs.com/YunQianQwQ/p/17492074.html