Time Limit: 10 Sec
Memory Limit: 64 MB
Description
小可可和小卡卡想到Y岛上旅游,但是他们不知道Y岛有多远。好在,他们找到一本古老的书,上面是这样说的: 下面是N个正整数,每个都在1~K之间。如果有两个数A和B,A在B左边且A大于B,我们就称这两个数为一个“逆序对”。你数一数下面的数字里有多少个逆序对,你就知道Y岛离这里的距离是多少千米了。 比如说,4 2 1 3 3里面包含了5个逆序对:(4, 2), (4, 1), (4, 3), (4, 3), (2, 1)。 可惜的是,由于年代久远,这些数字里有一部分已经模糊不清了,为了方便记录,小可可用“-1”表示它们。比如说,4 2 -1 -1 3 可能原来是4 2 1 3 3,也可能是4 2 4 4 3,也可能是别的样子。 小可可希望知道,根据他们看清楚的这部分数字,能不能推断出这些数字里最少能有多少个逆序对。
Input
第一行两个正整数N和K。第二行N个整数,每个都是-1或是一个在1~K之间的数。
Output
一个正整数,即这些数字里最少的逆序对个数。
HINT
100%的数据中,N<=10000,K<=100。
60%的数据中,N<=100。
40%的数据中,-1出现不超过两次。
题目分析
首先可以想到从左往右每个-1位置填写的数字一定单调不下降
若有
a
j
>
a
i
,
j
<
i
a_j>a_i,j<i
aj>ai,j<i,交换
a
i
,
a
j
a_i,a_j
ai,aj,逆序对数不可能增加
因为交换后
a
i
,
a
j
a_i,a_j
ai,aj对区间
[
i
,
n
]
[i,n]
[i,n]逆序对贡献不变,但减少了
a
j
,
a
i
a_j,a_i
aj,ai这一逆序对
且因为交换后
a
j
a_j
aj变小,原来
a
j
a_j
aj对区间
[
j
,
i
]
[j,i]
[j,i]的逆序对贡献可能消失
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示从右往左第
i
i
i个-1位置填
j
j
j增加的最小逆序对数
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
−
1
]
[
k
]
)
+
dp[i][j]=min(dp[i-1][k])+
dp[i][j]=min(dp[i−1][k])+第
i
i
i个-1位置填
j
j
j增加的逆序对数,其中
k
≥
j
k\geq j
k≥j
开一个数组
m
i
[
i
]
[
j
]
mi[i][j]
mi[i][j]保存
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]~
d
p
[
i
]
[
k
]
dp[i][k]
dp[i][k]的最小值就可以
O
(
K
)
O(K)
O(K)转移了
总复杂度 O ( n k ) O(nk) O(nk)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long lt;
#define lowbit(x) ((x)&(-x))
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int inf=1e9;
const int maxn=10010;
int n,K;
int a[maxn],sum[2][maxn],tot;
int rem[maxn];
lt dp[maxn][110],mi[maxn][110];
void add(int x,int v,int d){ for(int i=x;i<=K;i+=lowbit(i))sum[d][i]+=v;}
int qsum(int x,int d){ int res=0; for(int i=x;i>0;i-=lowbit(i))res+=sum[d][i]; return res;}
int main()
{
n=read();K=read();
for(int i=1;i<=n;++i)
{
a[i]=read();
if(a[i]!=-1) add(a[i],1,0);
else tot++;
}
lt ans=0; int cnt=0;
for(int i=n;i>=1;--i)
{
if(a[i]!=-1){
ans+=qsum(a[i]-1,1);
add(a[i],1,1); add(a[i],-1,0);
}
else{
++cnt;
for(int j=1;j<=K;++j) dp[cnt][j]=inf;
int tt=qsum(K,0);
for(int j=1;j<=K;++j)
rem[j]=tt-qsum(j,0)+qsum(j-1,1);
//qsum(j-1,1)求(i,n]内小于j的个数,qsum(K,0)-qsum(j,0)求[1,i)内大于j的个数
for(int j=1;j<=K;++j)
dp[cnt][j]=rem[j]+mi[cnt-1][j];
mi[cnt][K+1]=inf;
for(int j=K;j>=1;--j)
mi[cnt][j]=min(mi[cnt][j+1],dp[cnt][j]);
}
}
lt tt=inf;
for(int i=1;i<=K;++i) tt=min(tt,dp[tot][i]);
printf("%lld",ans+tt);
return 0;
}