题目
给你一个长为n(1<=n<=1e5)的序列a(0<=ai<=1e9),
m(1<=m<=1e5)次询问,每次查询一个区间[l,r]的逆序对数,可离线。
思路来源
登录 - 洛谷
三道经典分块题的更优复杂度解法&[Ynoi2019模拟赛]题解 - 博客 - OldDriverTree的博客
值域分块入门__ChiFAN_的博客-CSDN博客
题解
莫队二次离线:
1. 普通的莫队求区间逆序对个数,是莫队+树状数组求逆序对,复杂度
插入/删除一个值v时,实际要求当前区间[l,r]内,比v大/小的数的个数①,用树状数组求
而①可以继续离线,转化为求[1,r]比v大/小的数的个数-[1,l-1]比v大/小的数的个数
共有个莫队询问,个前缀离线修改,
所以,需要一个的插入,O(1)的查询的数据结构,这就是值域分块
a[i]表示i位置的值,普通的莫队/分块都是按i分块,也就是按位置分块
而值域分块:按值的出现次数分块
cnt[i]:i这个值出现的次数,每个出现次数分一块,
这样就能实现的插入,O(1)的查询了
维护块内的个数的前缀和/后缀和,块间的个数的前缀和/后缀和,
插入时修改个块+块内位置,查询O(1)查
复杂度
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int N=100005;
int n,m;ll a[N];
inline int lowbit(int x){return x&-x;}
ll c[N],sz=0;
int p[N];bool cmp(int x,int y){return a[x]<a[y];}
inline void add(int x,ll k){sz+=k;for(;x<=n;x+=lowbit(x))c[x]+=k;}
inline ll getsum(int x){ll ans=0;for(;x;x-=lowbit(x))ans+=c[x];return ans;}
ll L[N],R[N];//L[i]:1~i-1有多少个数大于a[i];R[i]:i+1~n有多少个数小于a[i]
const int block=310;
struct ASK
{
int l,r,p;
}ask[N];
inline bool mmp(ASK n1,ASK n2)
{
if(n1.l/block!=n2.l/block)return n1.l<n2.l;
if((n1.l/block)&1)return n2.r<n1.r;
return n1.r<n2.r;
}
struct node{int l,r,p,op;};
vector<node>ls[N],rs[N];
int B,bl[N/block+5],br[N/block+5],w[N];
int s[N/block+5],C[N];
ll ret[N],ans[N];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
p[i]=i;
}
sort(p+1,p+n+1,cmp);
ll tmp=a[p[1]];a[p[1]]=1;
for(int i=2,t=1;i<=n;i++)
{
if(a[p[i]]!=tmp)t++;
tmp=a[p[i]];a[p[i]]=t;
}//离散化
for(int i=1;i<=n;i++)
{
L[i]=L[i-1]+sz-getsum(a[i]);
add(a[i],1);//L[i]转前缀和
}//for(int i=1;i<=n;i++)printf("%lld ",L[i]);puts("");
memset(c,0,sizeof(c));
for(int i=n;i>=1;i--)
{
R[i]=R[i+1]+getsum(a[i]-1);
add(a[i],1);//R[i]转后缀和
}//for(int i=1;i<=n;i++)printf("%lld ",R[i]);puts("");
for(int i=1;i<=m;i++)
{
scanf("%d%d",&ask[i].l,&ask[i].r);
if(ask[i].l>ask[i].r)swap(ask[i].l,ask[i].r);
ask[i].p=i;
}
sort(ask+1,ask+m+1,mmp);//排序
ask[0]=(ASK){1,0,0};
for(int i=1;i<=m;i++)//莫队二次离线
{//把莫队的移动离线下来
ret[i]=L[ask[i].r]-L[ask[i-1].r]+R[ask[i].l]-R[ask[i-1].l];
if(ask[i].r>ask[i-1].r)rs[ask[i-1].l-1].push_back((node){ask[i-1].r+1,ask[i].r,i,-1});
else if(ask[i].r<ask[i-1].r)rs[ask[i-1].l-1].push_back((node){ask[i].r+1,ask[i-1].r,i, 1});
if(ask[i].l<ask[i-1].l)ls[ask[i ].r+1].push_back((node){ask[i].l,ask[i-1].l-1,i,-1});
else if(ask[i].l>ask[i-1].l)ls[ask[i ].r+1].push_back((node){ask[i-1].l,ask[i].l-1,i, 1});
}
B=(n-1)/block+1;
for(int i=1;i<=B;i++)
{
bl[i]=br[i-1]+1;
br[i]=br[i-1]+block;
}br[B]=n;//值域分块
for(int i=1;i<=B;i++)for(int j=bl[i];j<=br[i];j++)w[j]=i;
for(int i=1;i<=n;i++)
{
for(int j=1;j<w[a[i]];j++)s[j]++;
for(int j=bl[w[a[i]]];j<=a[i];j++)C[j]++;
for(int j=0;j<rs[i].size();j++)
{
node t=rs[i][j];
int l=t.l,r=t.r;
tmp=0;
for(int k=l;k<=r;k++)tmp+=s[w[a[k]+1]]+C[a[k]+1];
ret[t.p]+=t.op*tmp;
}
}
memset(C,0,sizeof(C));
memset(s,0,sizeof(s));
for(int i=n;i>=1;i--)
{
for(int j=w[a[i]]+1;j<=B;j++)s[j]++;
for(int j=a[i];j<=br[w[a[i]]];j++)C[j]++;
for(int j=0;j<ls[i].size();j++)
{
node t=ls[i][j];
int l=t.l,r=t.r;
tmp=0;
for(int k=l;k<=r;k++)tmp+=s[w[a[k]-1]]+C[a[k]-1];
ret[t.p]+=t.op*tmp;
}
}
for(int i=1;i<=m;i++)
{
ret[i]+=ret[i-1];//ret最终的值是差分值
ans[ask[i].p]=ret[i];
}
for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
return 0;
}