看数据范围显然是 DP
但简单推推会发现:怎么搞都有后效性!!当前填数必然会对后面的产生影响,而 前面所有数的状态 是不可能在 DP 里记录的
一般遇到这种情况时,我们就要考虑 通过推性质,运用贪心策略排除无用状态,使得剩下的转移无后效性
通过 打表、感性理解、理性证明,我们发现一个性质:
从左往右所填的数一定单调不升
简单的证明:
形式化的,对于
i
<
j
i < j
i<j 且
a
[
i
]
=
a
[
j
]
=
0
a[i]=a[j]=0
a[i]=a[j]=0,若所填数
b
b
b 满足
b
[
i
]
<
b
[
j
]
b[i]<b[j]
b[i]<b[j] 则一定不优
考虑邻项交换,交换后, i i i 之前 大于 b [ i ] b[i] b[i] 的仍然构成逆序对, j j j 之后小于 b [ j ] b[j] b[j] 的仍然构成
而在
i
,
j
i,j
i,j 之间,大于
b
[
j
]
b[j]
b[j] 的更可以大于
b
[
i
]
b[i]
b[i],小于
b
[
i
]
b[i]
b[i] 的更小于
b
[
j
]
b[j]
b[j],除此以外还可能有新增的贡献
该性质得证
推出这一点,我们就可以 DP 了, f [ i ] [ j ] f[i][j] f[i][j] 表示考虑了前 i i i 个数,填到 j j j,且 i i i 为最后一个连续段的结尾,最多逆序对
为了方便计算,我们先统计 所有不为零的位置 两两间的贡献,接下来每确定填一个数,就加上其与前后产生的总贡献
转移有: f [ i ] [ j ] = max 0 ≤ k ≤ i ( f [ k ] [ j + 1 ] + v a l [ i ] [ j ] − v a l [ k ] [ j ] + ( i − k ) k ) f[i][j]=\max_{0\leq k\leq i}(\ f[k][j+1]+val[i][j]-val[k][j]+(i-k)k\ ) f[i][j]=0≤k≤imax( f[k][j+1]+val[i][j]−val[k][j]+(i−k)k )
其中 v a l [ i ] [ j ] val[i][j] val[i][j] 表示 前 i i i 个 零的位置都填 j j j ,总贡献(这种前缀思想很重要),是可以 O ( n k ) O(nk) O(nk) 预处理的
对式子变形: f [ k ] [ j + 1 ] − v a l [ k ] [ j ] − k 2 = − i k − v a l [ i ] [ j ] + f [ i ] [ j ] f[k][j+1]-val[k][j]-k^2=-ik-val[i][j]+f[i][j] f[k][j+1]−val[k][j]−k2=−ik−val[i][j]+f[i][j]
再复习一遍:只与 k k k 有关的作为 Y Y Y, i i i 与 k k k 的乘积作为 k x kx kx,所求值 和 常数作为 b b b
直接上斜率优化即可,注意初值 inf 的问题
#include<bits/stdc++.h>
using namespace std ;
#define Y(k) (-f[k][j+1]+val[k][j]+1LL*k*k)
#define X(k) (k)
#define tt q.size()-1
typedef long long LL ;
const int N = 2e5+10 , M = 105 ;
int n , K ;
int a[N] , len , g[N] , sum[M] , sumh[M] ;
LL f[N][M] , val[N][M] , ans ;
void pre_work()
{
for(int i = 1 ; i <= n ; i ++ ) {
if( a[i] ) {
ans += sum[a[i]] ;
for(int j = 1 ; j < a[i] ; j ++ ) {
sum[j] ++ ;
}
for(int j = a[i]+1 ; j <= K ; j ++ ) {
sumh[j] -- ;
}
continue ;
}
g[++len] = i ;
for(int j = 1 ; j <= K ; j ++ ) {
val[len][j] = val[len-1][j] + sum[j] + sumh[j] ;
}
}
}
deque<int> q ;
int main()
{
scanf("%d%d" , &n , &K ) ;
for(int i = 1 ; i <= n ; i ++ ) {
scanf("%d" , &a[i] ) ;
if( a[i] ) for(int j = a[i]+1 ; j <= K ; j ++ ) sumh[j] ++ ;
}
pre_work() ;
memset( f , 0xcf , sizeof f ) ;
for(int j = K ; j >= 1 ; j -- ) {
q.clear() ;
f[0][j+1] = 0 ;
q.push_back(0) ;
for(int i = 1 ; i <= len ; i ++ ) {
int kk = i ;
while( q.size()>1 && Y(q[1])-Y(q[0]) <= (X(q[1])-X(q[0]))*kk ) q.pop_front() ;
int k = q.front() ;
f[i][j] = max( f[i][j+1] , f[k][j+1] + val[i][j] - val[k][j] + LL(i-k)*k ) ;
if( f[i][j+1] >= 0 ) { // 合法才加
while( q.size()>1 && (Y(q[tt])-Y(q[tt-1]))*(X(i)-X(q[tt])) >= (Y(i)-Y(q[tt]))*(X(q[tt])-X(q[tt-1])) ) q.pop_back() ;
q.push_back( i ) ;
}
}
}
printf("%lld\n" , max(0LL,f[len][1])+ans ) ;
return 0 ;
}