一种基于容斥原理的n^2logn后优化求全子区间逆序对
RimingMist · · 算法·理论
零、题外话
考场上自己乱想的,甚至其中的一个步骤我都不能解释清楚为什么这么做 (因为是我自己手模出来的)。
好,回归正题。
我这里的“区间逆序对”其实不是标准的。当我们需要 每一个区间 的逆序对的时候,我这种神秘的 这就导致了我考场上 MLE 了两个点,并且是捆绑测试…
这里由于时间复杂度和空间复杂度不够优秀,于是我进行了更新,优化掉了一个
一、求逆序对的公式
这个算法基于一个公式,接下来,我们定义:
-
-
- 为了过审,我们定义
\text{inv}(C) 表示序列C 的逆序对个数。
假设我们求
怎么感觉这个 markdown 怪怪的
其实核心部分我已经讲完了…
给大家讲讲原理:
基于容斥原理。
众所周知,如果要求
这个也差不多,前面的
这里是我最疑惑的。我们想要从多余中找到区间逆序对,不能直接用容斥原理,因为
其实就是一个简单(kun nan)容斥原理。
接下来我们试试怎么编码。
二、代码实现
为了求逆序对,我使用了树状数组:
int mp[5002];
inline void update(int x,int v){
while(x<=10002)mp[x]+=v,x+=lowbit(x);
}
inline ll query(int x){
ll res=0;
while(x)res+=mp[x],x-=lowbit(x);
return res;
}
以及一些数组:
ll fan[5002];//fan[i]代表数组a[i]~a[n]的逆序对个数
ll zheng[5002];//zheng[i]代表数组a[1]~a[i]的逆序对个数
ll node[5002][5003];//node[i][j]代表a[j]~a[n]中比i小的。注意,这里是i,不是a[i]
ll all[5002][5002];//all[i][j]代表{a[1],a[r]~a[n]}的逆序对个数+...+{a[l],a[r]~a[n]}的逆序对个数
ll nx[5002][5003];//nx[i][j]代表a[i]~a[j]的逆序对个数
这里我不得不讲一讲
第一部分:求
int n;cin>>n;
pair<int,int> in[n+1];int a[n+1];
for(int i=1;i<=n;i++) cin>>in[i].first,in[i].second=i;
sort(in+1,in+1+n);
int tmp=0;
for(int i=1;i<=n;i++){
if(i==1||in[i].first!=in[i-1].first){
tmp++;
}a[in[i].second]=tmp;
}//离散化
//求整体和fan逆序对
ll num=0;//num存整体
for(int i=n;i>=1;i--){
ll tmp=query(a[i]-1);
fan[i]+=tmp+fan[i+1];//这个很好理解吧...后面一个加上当前点产生的逆序对个数
num+=tmp;
update(a[i],1);
}
第二部分:求
memset(mp,0,sizeof mp);//清空树状数组
for(int i=1;i<=n;i++){
update(a[i],1);//再来一次
zheng[i]=zheng[i-1]+query(10005)-query(a[i]);//其实就是正着算逆序对的方法。时间复杂度比反着算差一点。
}
最难算的就是
第三部分:求 node。这里就是时间复杂度的瓶颈了,是
memset(mp,0,sizeof mp);//清空树状数组
for(int i=n;i>=1;i--){
update(a[i],1);//倒着求,一样的
for(int j=1;j<=i-1;j++){//node是为了求all的,不需要每一个数字,所以优化了这里。不然每一次都要跑n次。
node[a[j]][i]=query(a[j]-1);//看目前在i之后比a[j]小的。(因为求到了i这个位置)
}
}
第四部分:求 all。
for(int i=n;i>=1;i--){
ll tmp=0;//记录目前算下来有几个符合的
for(int j=1;j<i;j++){
tmp+=node[a[j]][i];//加上第i位之后比a[j]小的
all[j][i]=tmp;//因为是累加,所以可以直接得到
}
}
第五部分:求 nx。直接套用公式就可以了。
for(int l=1;l<=n;l++){
for(int r=l+1;r<=n;r++){
nx[l][r]=(zheng[r]+fan[l]+all[l-1][r+1]-num);//就是那个公式
}
}
nx 中就存储了每一段区间的值啦!
这个算法可能并非时间复杂度是最优秀的,但是这是一种思维方式,即区间计数问题可以考虑使用容斥原理。这份代码或许有优化的地方,Gemini 说可以区间 DP 是
这是我的废稿(格式不对):一种神秘的 n^2 求每一个区间的逆序对(稿子)