\(\mathcal{Description}\)

  Link.

  给定升序序列 \(\{x_n\}\) 以及整数 \(k\),在 \(\{x_n\}\) 中选出恰 \(k\) 对 \((x_i,x_j)\),使得不存在某个值出现次数多于一次,并最小化 \(\sum|x_i-x_j|\)。

\(\mathcal{Solution}\)

  告诉我,你有一个错误的贪心 owo!

  显然 \((x_i,x_j)\) 是相邻的两个数。令 \(a_i=x_{i+1}-x_i\),问题转化为选 \(k\) 个 \(a_i\) 使其和最小,并保证 \(a_i\) 被选后 \(a_{i-1}\) 和 \(a_{i+1}\) 不被选。

  贪心取最小是不可取的,样例就是反例。不过可以使用网络流退流的思想挽救这个贪心。每次取出最小值 \(a_i\) 时,将 \(a_i\) 的值置为 \(a_{i-1}+a_{i+1}-a_i\) 并重新入堆,同时删除在序列上 \(a_{i-1}\) 和 \(a_{i+1}\)(这里的下标加减法指前驱后继,因为有些数已经被删掉了)。考虑再次选择 \(a_i\) 时所表达的方案:

  初始:

\[\require{cancel}a_{i-2}~~~~a_{i-1}~~~~a_i~~~~a_{i+1}~~~~a_{i+2}
\]

  选 \(a_i\),此时答案 \(ans=a_i\);并重置 \(a_i\),删前驱后继:

\[\require{cancel}a_{i-2}~~~~\cancel{a_{i-1}}~~~~a_{i-1}+a_{i+1}-a_i~~~~\cancel{a_{i+1}}~~~~a_{i+2}
\]

  再选 \(a_i\),此时答案 \(ans=a_i+a_{i-1}+a_{i+1}-a_i=a_{i-1}+a_{i+1}\),再重置,删除:

\[\require{cancel}\cancel{a_{i-2}}~~~~\cancel{a_{i-1}}~~~~a_{i-2}+a_{i+2}-a_{i-1}-a_{i+1}+a_i~~~~\cancel{a_{i+1}}~~~~\cancel{a_{i+2}}
\]

  可以发现,这与直接选 \(a_{i-2}\) 和 \(a_{i+2}\) 是等效的!所以维护一个双向链表,利用堆进行贪心即可。

  复杂度 \(\mathcal O(n\log n)\)。

\(\mathcal{Code}\)

#include <queue>
#include <cstdio> typedef long long LL;
typedef std::pair<LL, int> pli; const int MAXN = 1e5;
int n, K, pre[MAXN + 5], suf[MAXN + 5];
LL val[MAXN + 5];
bool rmd[MAXN + 5];
std::priority_queue<pli, std::vector<pli>, std::greater<pli> > heap; inline int rint () {
int x = 0; char s = getchar ();
for ( ; s < '0' || '9' < s; s = getchar () );
for ( ; '0' <= s && s <= '9'; s = getchar () ) x = x * 10 + ( s ^ '0' );
return x;
} inline void rmpos ( const int u ) {
if ( ! u || u == n ) return ;
rmd[u] = true;
if ( pre[u] ) suf[pre[u]] = suf[u];
if ( suf[u] ^ n ) pre[suf[u]] = pre[u];
pre[u] = suf[u] = 0;
} int main () {
n = rint (), K = rint ();
for ( int i = 0, p, las; i < n; ++ i ) {
p = rint ();
if ( i ) {
heap.push ( { val[i] = p - las, i } );
pre[i] = i - 1, suf[i] = i + 1;
}
las = p;
}
LL ans = 0;
val[0] = val[n] = 1ll << 60;
while ( K -- ) {
pli t = heap.top (); heap.pop ();
if ( rmd[t.second] ) { ++ K; continue; }
ans += t.first; LL nv = -t.first;
nv += val[pre[t.second]], rmpos ( pre[t.second] );
nv += val[suf[t.second]], rmpos ( suf[t.second] );
heap.push ( { val[t.second] = nv, t.second } );
}
printf ( "%lld\n", ans );
return 0;
}

最新文章

  1. .NET开源进行时:消除误解、努力前行(本文首发于《程序员》2015第10A期的原始版本)
  2. HBase 的表结构
  3. java导出word的6种方式(复制来的文章)
  4. Java实习生面试总结
  5. PHP导出数据到excel的方法
  6. Mysql数据库的使用经验总结
  7. phalcon开发工具(phalcon-devtools)
  8. Unity3D多人协作开发环境搭建
  9. iOS 苹果自带地图定位Core Location
  10. CSS3实战开发: 纯CSS实现图片过滤分类显示特效
  11. ZOJ 1655 FZU 1125 Transport Goods
  12. 《大型网站技术架构:核心原理与案例分析》【PDF】下载
  13. HBase rebalance 负载均衡源码角度解读使用姿势
  14. django项目实现中文检索
  15. pl/sql学习(4): 包package
  16. Dynamics 365—脚本
  17. python中的 set 中的元素
  18. 20171024xlVBA批量获取PPT\WORD\PDF页数
  19. 解决VMware虚拟机网络时长中断的问题
  20. Go Session 使用简介

热门文章

  1. Vue项目中使用websocket
  2. LCT小记
  3. Vue 动态设置图片路径
  4. ComboBox行高
  5. 【记录一个问题】铁威马NAS存储中的人人影视APP,其WEB服务占满一个CPU核
  6. 搭建服务器之DNS
  7. 如何理解python中的cmp_to_key()函数
  8. HTTPS加密证书(1)
  9. python17day
  10. 微前端框架 之 qiankun 从入门到源码分析