1 条题解
-
0
找出性质 DP。好题!
结论:最终序列 由若干个连续等差下降子段构成,其中公差为 。(对于上升的情况,我们可以认为是若干个长度为 的下降子段)
考虑证明:首先 是一个排列。注意到 即可。
套路地,考虑设计一个 dp。设 为值域 已经分成若干个连续下降子段(可以认为是放在结果序列 的前 个位置)的最小交换次数。套路地写出转移:
$$f(i)=\min _{j\in [0,i)} \{f(j)+\mathrm{calc}(j+1,i)\}$$其中 表示将 值域排列成一个连续下降子段的最小交换次数。
现在的序列中,位置 已经被值域 占据,所以 已经被考虑过了,不能重复计算。我们只需要考虑 这段值域,目标是:让值域 占据位置 。
以下默认 。
我们可以认为,操作是这么进行的:先将值域为 的数摆在位置 ,再交换使其变成下降的。
将值域为 的数染成 ,值域为 的数染成 ,我们要让 占据位置 的前一段,并且 之间不互相交换。这部分答案就是 之间的逆序对数。也就是:
$$\sum_{i=1}^n \sum_{j=i+1}^n [a_i\gt r][l\le a_j\le r]$$最后,交换成下降序列,交换次数就是顺序对数,也就是:
$$\sum_{i=1}^n\sum_{j=i+1}^n [a_i\in [l,r]][a_j\in [l,r]][a_i\lt a_j]$$朴素地实现 可以获得 分。注意到:这是一个二维数点问题。例如,对于顺序对 ,视为 上有一个点,那么每次查询就是查询 , 为顶点的正方形内的点的数量,可以二维前缀和维护。
这样,我们就做到了 。代码。
#include <bits/stdc++.h> using namespace std; constexpr int MAXN=5e3+10; int a[MAXN], f[MAXN], n; int g[MAXN][MAXN], h[MAXN][MAXN]; signed main() { cin>>n; for (int i=1; i<=n; ++i) cin>>a[i]; for (int i=1; i<=n; ++i) for (int j=i+1; j<=n; ++j) { g[min(a[i],a[j])][max(a[i],a[j])]+=a[i]<a[j], h[min(a[i],a[j])][max(a[i],a[j])]+=a[i]>a[j]; } for (int i=1; i<=n; ++i) for (int j=1; j<=n; ++j) g[i][j]+=g[i][j-1]+g[i-1][j]-g[i-1][j-1], h[i][j]+=h[i][j-1]+h[i-1][j]-h[i-1][j-1]; memset(f,0x3f,sizeof f); f[0]=0; auto calc=[](int l, int r) { int ans=g[r][r]-g[l-1][r]-g[r][l-1]+g[l-1][l-1]; if (r==n) return ans; ans+=h[r][n]-h[r][r]-h[l-1][n]+h[l-1][r]; return ans; }; for (int i=1; i<=n; ++i) for (int j=0; j<i; ++j) f[i]=min(f[i],f[j]+calc(j+1,i)); cout<<f[n]<<'\n'; }
- 1
信息
- ID
- 9042
- 时间
- 4000ms
- 内存
- 512MiB
- 难度
- 9
- 标签
- 递交数
- 15
- 已通过
- 4
- 上传者