题目
题解
这道题目我觉得是神题,非常有意思的题目,也挺难做的。
第一问:求最少需要改变的数量
这一问比较简单,之前也见过这种套路。
定义
表示
序列中
部分构成严格递增子序列且以
为结尾所不需要改变的最大的数量。
那么转移方程就是:
那么我们稍微对转移方程做一个变形:
我们发现如果让
的话,这就是一个最长非降子序列,我们可以使用二分的方法在
的时间解决。
即是整个
序列能构成最长非降子序列的长度值。
那么第一问的答案就是
。
在我们求解最长非减子序列的时候,我们需要处理出一个数组
,
表示以以
结尾的最长非减序列的长度。以及
个链表
(我在代码里用vector表示的),
表示满足
的第
大的
。
用这些处理好的信息,我们就可以来做第二问了。
第二问:求改变的最小花费
在求改变的最小花费的时候我们仍然使用
的方法来解决。
我们定义
表示
已经被修改成为最长非减子序列,并且末尾的
没有被修改,所花费的最大值。
那么转移方程可以写成:
这个
表示将
中的所有元素都修改成为介于
之间的元素所花费的最小值。
我们来计算这个
。
首先,对于所有
要么
,要么
。
因此我们所采取的最优策略就是找出一个分界点
,使得
中的元素全都等于
使得
中的元素全部都等于
,其中
。
那么
这样的话,我们枚举
就可以把这道题目在
的时间复杂度内解决掉。
下面我们看看能不能优化。
枚举
肯定不能省掉,对
,我们再开始枚举
,注意,我们直接在
链表中找到满足条件的
,而且我们枚举
的顺序是按照
从大到小的顺序,这样的话,我们可以顺手记录一个
,表示当前把
之间的元素全部都改变到
了。即
。
然后假设最优决策点是
,其中
。再假设
之间有
个小于
,有
个大于
,
那么
即
想要使得
最小,必须让
最大。
但实际上要求 位置开始的前缀的 的最大值不好求,我们可以反过来想,当 和 固定的时候, 的值也就固定了,我们可以通过求从 开始的后缀中 的最小值。
这样这个题就可以在 的最差时间复杂度内解决掉了。
由于题目保证随机数据,所以 根本不可能跑满。
事实上还跑的很快。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn = 1e5;
const ll inf = 0x3f3f3f3f;
typedef pair<int,ll> pii;
int n;
ll dp[maxn],a[maxn],f[maxn];
vector<int> nxt[maxn];
ll dp2[maxn];
int dfs(int u){
if(dp2[u] != -1) return dp2[u];
int ni = u;
ll sum = 0,z = 0,y = 0;
ll mizy = 0,res = 0;
for(int i = nxt[f[u]-1].size()-1;i >= 0;--i){
int nj = nxt[f[u]-1][i];
if(nj > u || a[nj] > a[u]) continue;
for(int j = ni;j > nj;--j){
sum += abs(a[j] - a[u]);
if(a[j] < a[u]) z++;
if(a[j] > a[u]) y++;
mizy = min(mizy,z-y);
}
ni = nj;
int rr = sum - (a[u]-a[nj])*(z-y-mizy)+dfs(nj);
if(!res || rr < res) res = rr;
}
return dp2[u] = res;
}
int main()
{
memset(dp2,-1,sizeof(dp2));
scanf("%d",&n);
for(int i = 1;i <= n;++i) scanf("%lld",&a[i]),a[i] -= i;
a[0] = -inf,a[n+1] = inf;
memset(dp,inf,sizeof(dp));
for(int i = 1;i <= n;++i){
int loc = upper_bound(dp+1,dp+1+n,a[i]) - dp;
dp[loc] = a[i];
f[i] = loc;
nxt[f[i]].push_back(i);
}
int mxlen = lower_bound(dp+1,dp+1+n,inf) - dp - 1;
cout<<n - mxlen<<endl;
a[n+1] = inf;f[n+1] = mxlen+1;
dp2[0] = 0;
ll ans = dfs(n+1);
cout<<ans<<endl;
}