题目
题目链接
题解
这道题目我觉得是神题,非常有意思的题目,也挺难做的。
第一问:求最少需要改变的数量
这一问比较简单,之前也见过这种套路。
定义dp[i]dp[i]表示aa序列中部分构成严格递增子序列且以a[i]a[i]为结尾所不需要改变的最大的数量。
那么转移方程就是:
dp[i]=min1≤j<i,a[i]−a[j]>=i−j(dp[j]+1)dp[i]=min1≤j<i,a[i]−a[j]>=i−j(dp[j]+1)
那么我们稍微对转移方程做一个变形:
dp[i]=min1≤j<i,a[i]−i≥a[j]−j(dp[j]+1)dp[i]=min1≤j<i,a[i]−i≥a[j]−j(dp[j]+1)
我们发现如果让a[i]−=ia[i]−=i的话,这就是一个最长非降子序列,我们可以使用二分的方法在O(nlogn)O(nlogn)的时间解决。
mxlenmxlen即是整个aa序列能构成最长非降子序列的长度值。
那么第一问的答案就是。
在我们求解最长非减子序列的时候,我们需要处理出一个数组ff,表示以以a[i]a[i]结尾的最长非减序列的长度。以及mxlenmxlen个链表nxtnxt(我在代码里用vector表示的),nxt[i][j]nxt[i][j]表示满足f[x]=if[x]=i的第jj大的。
用这些处理好的信息,我们就可以来做第二问了。
第二问:求改变的最小花费
在求改变的最小花费的时候我们仍然使用dpdp的方法来解决。
我们定义dp2[i]dp2[i]表示a[1,i]a[1,i]已经被修改成为最长非减子序列,并且末尾的a[i]a[i]没有被修改,所花费的最大值。
那么转移方程可以写成:
dp2[i]=min0≤j<i,f[i]=f[j]+1(dp2[j]+cost(j+1,i−1))dp2[i]=min0≤j<i,f[i]=f[j]+1(dp2[j]+cost(j+1,i−1))
这个cost(j+1,i−1)cost(j+1,i−1)表示将a[j+1,i−1]a[j+1,i−1]中的所有元素都修改成为介于[a[j],a[i]][a[j],a[i]]之间的元素所花费的最小值。
我们来计算这个cost(j+1,i−1)cost(j+1,i−1)。
首先,对于所有a[x]a[x]要么>a[i]>a[i],要么 <a[j]<a[j]<script type="math/tex" id="MathJax-Element-17815">< a[j]</script>。
因此我们所采取的最优策略就是找出一个分界点kk,使得中的元素全都等于a[j]a[j]使得a[k+1,i]a[k+1,i]中的元素全部都等于a[i]a[i],其中j≤k<ij≤k<i。
那么
cost(j+1,i−1)=minj≤k<i(∑j≤x≤k|a[x]−a[j]|+∑k<x<i|a[x]−a[i]|)cost(j+1,i−1)=minj≤k<i(∑j≤x≤k|a[x]−a[j]|+∑k<x<i|a[x]−a[i]|)
这样的话,我们枚举i,j,ki,j,k就可以把这道题目在O(n3)O(n3)的时间复杂度内解决掉。
下面我们看看能不能优化。
枚举ii肯定不能省掉,对,我们再开始枚举jj,注意,我们直接在链表中找到满足条件的jj,而且我们枚举的顺序是按照jj从大到小的顺序,这样的话,我们可以顺手记录一个,表示当前把a[j+1,i−1]a[j+1,i−1]之间的元素全部都改变到a[i]a[i]了。即sum=∑j<x≤i|a[x]−a[i]|sum=∑j<x≤i|a[x]−a[i]|。
然后假设最优决策点是kk,其中。再假设a[j+1,k]a[j+1,k]之间有zz个小于,有yy个大于,
那么cost(j+1,i−1)=sum−(a[i]−a[j])∗z+(a[i]−a[j])∗ycost(j+1,i−1)=sum−(a[i]−a[j])∗z+(a[i]−a[j])∗y
即cost(j+1,i−1)=sum−(a[i]−a[j])∗(z−y)cost(j+1,i−1)=sum−(a[i]−a[j])∗(z−y)
想要使得costcost最小,必须让z−yz−y最大。
但实际上要求jj位置开始的前缀的的最大值不好求,我们可以反过来想,当ii和固定的时候,z−yz−y的值也就固定了,我们可以通过求从ii开始的后缀中的最小值。
这样这个题就可以在O(n2+nlogn)O(n2+nlogn)的最差时间复杂度内解决掉了。
由于题目保证随机数据,所以n2n2根本不可能跑满。
事实上还跑的很快。
代码
#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;
}