题解:P7406 [JOI 2021 Final] 集体照 / Group Photo
又没看懂题解,于是写了一份特别奇怪的代码,然后过了。
于是我准备写一篇非常详细的题解。
设排序后在第
性质 1:如果最终序列中某段等差数列的结尾为
性质 2:对于某一值域内的交换不会影响其他不在至于内的数的顺序,也就是说交换可能改变实际位置,但步改变值域外的数的相对位置。
性质 3:根据性质
因此我们现在需要预处理两个东西,一个是值域
然后就到了 dp 部分,设
#include<bits/stdc++.h>
#define mp make_pair
#define fi first
#define se second
#define lb lower_bound
#define fr front()
#define pii pair<int,int>
#define INF 1e18
using namespace std;
int n,a[5005],f[5005][5005],g[5005][5005],h[5005];
int p[5005][5005],c[5005][5005];
void solve1(){
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if(a[i]<a[j]){
g[a[i]][a[j]]++;
p[a[i]][a[j]]++;c[a[j]][a[i]]++;
}
}
}
for(int i=1;i<=n;i++){
for(int j=i+2;j<=n;j++)p[i][j]+=p[i][j-1];
for(int j=i-2;j>=1;j--)c[i][j]+=c[i][j+1];
}
for(int len=2;len<=n;len++){
for(int i=1;i+len-1<=n;i++){
int l=i,r=i+len-1;
g[l][r]+=g[l+1][r-1]+p[l][r-1]+c[r][l+1];
//中间部分,包含左端点的,包含右端点的
}
}
}//统计顺序对部分
void solve2(){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)p[i][j]=0,c[i][j]=0;
}
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if(a[i]>a[j])p[a[j]][a[i]]++;
}
}
for(int i=1;i<=n;i++){
for(int j=n;j>=1;j--)p[i][j]+=p[i][j+1];
}
//这样统计出来了i与[j+1,n]的逆序对数
for(int i=n-1;i>=1;i--){
for(int j=n-1;j>=i;j--){
c[i][j]=c[i+1][j]+p[i][j+1];
}
}
}//统计逆序对部分
signed main(){
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
solve1();solve2();
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++)f[i][j]=INF;
h[i]=INF;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
f[i][j]=h[j-1]+g[j][i]+c[j][i];
}
for(int j=1;j<=i;j++)h[i]=min(h[i],f[i][j]);
}
cout<<h[n];
}