【POJ Challenge】生日礼物

题目大意:给定一个长度为$n$的序列,允许选择不超过$m$个连续的部分,求元素之和的最大值。

数据范围:$1\le n, m\le 10^5$。


题解

显然的一步转化,就是把连续的、同符号的元素求和变成一个。

这样就变成了一串正负号交替的序列。

现在把所有正数都加一起,如果满足条件就直接输出。

不满足的话,我们发现:

我们可以选取一个负数,这样可以合并左右两个正数。

我们也可以删掉一个正数。

以上两个操作,都会使我们的选取的个数$-\ -$。

至于到底应该怎么选呢?

就弄一个堆,每次拿出来代价最小的操作就好。

代码

  1. #include <bits/stdc++.h>
  2.  
  3. #define N 100010
  4.  
  5. using namespace std;
  6.  
  7. int a[N], b[N], nxt[N], pre[N];
  8.  
  9. bool vis[N];
  10.  
  11. char *p1, *p2, buf[100000];
  12.  
  13. #define nc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++ )
  14.  
  15. int rd() {
  16. int x = 0, f = 1;
  17. char c = nc();
  18. while (c < 48) {
  19. if (c == '-')
  20. f = -1;
  21. c = nc();
  22. }
  23. while (c > 47) {
  24. x = (((x << 2) + x) << 1) + (c ^ 48), c = nc();
  25. }
  26. return x * f;
  27. }
  28.  
  29. struct Node {
  30. int val, id;
  31. friend bool operator < (const Node &a, const Node &b) {
  32. return a.val > b.val;
  33. }
  34. };
  35.  
  36. priority_queue<Node> q;
  37.  
  38. int main() {
  39. int n = rd(), m = rd();
  40. for (int i = 1; i <= n; i ++ ) {
  41. b[i] = rd();
  42. }
  43. int n1 = 1;
  44. a[1] = b[1];
  45. for (int i = 2; i <= n; i ++ ) {
  46. if ((a[n1] <= 0 && b[i] <= 0) || (a[n1] >= 0 && b[i] >= 0)) a[n1] += b[i];
  47. else a[ ++ n1] = b[i];
  48. }
  49. if (a[n1] <= 0) {
  50. n1 -- ;
  51. }
  52. if (a[1] <= 0) {
  53. for (int i = 1; i < n1; i ++ ) {
  54. a[i] = a[i + 1];
  55. }
  56. n1 -- ;
  57. }
  58. n = n1;
  59. int ans = 0, sum = 0;
  60. for (int i = 1; i <= n; i ++ ) {
  61. if (a[i] > 0) {
  62. sum ++ ;
  63. ans += a[i];
  64. }
  65. Node mdl;
  66. mdl.val = abs(a[i]);
  67. mdl.id = i;
  68. q.push(mdl);
  69. nxt[i] = i + 1;
  70. pre[i] = i - 1;
  71. a[i] = abs(a[i]);
  72. }
  73. // cout << ans << endl ;
  74. // cout << sum << endl ;
  75. nxt[n] = pre[1] = 0;
  76. if (sum <= m) {
  77. cout << ans << endl ;
  78. return 0;
  79. }
  80. m = sum - m;
  81. for (int i = 1; i <= m; i ++ ) {
  82. Node mdl = q.top();
  83. q.pop();
  84. while (vis[mdl.id] && !q.empty()) {
  85. mdl = q.top();
  86. q.pop();
  87. }
  88. // cout << mdl.val << endl ;
  89. if (vis[mdl.id])
  90. break;
  91. ans -= mdl.val;
  92. if (q.empty())
  93. break;
  94. int tmp = mdl.id;
  95. if (!pre[tmp]) {
  96. vis[tmp] = true;
  97. vis[nxt[tmp]] = true;
  98. pre[nxt[nxt[tmp]]] = 0;
  99. }
  100. else if(!nxt[tmp]) {
  101. vis[tmp] = true;
  102. vis[pre[tmp]] = true;
  103. nxt[pre[pre[tmp]]] = 0;
  104. }
  105. else {
  106. vis[nxt[tmp]] = true;
  107. vis[pre[tmp]] = true;
  108. mdl.val = a[tmp] = a[nxt[tmp]] + a[pre[tmp]] - a[tmp];
  109. if (nxt[nxt[tmp]])
  110. pre[nxt[nxt[tmp]]] = tmp;
  111. if (pre[pre[tmp]])
  112. nxt[pre[pre[tmp]]] = tmp;
  113. pre[tmp] = pre[pre[tmp]];
  114. nxt[tmp] = nxt[nxt[tmp]];
  115. q.push(mdl);
  116. }
  117. }
  118. cout << ans << endl ;
  119. return 0;
  120. }

小结:这玩意儿好像叫模拟费用流吧,不会不会有空学/cy

[bzoj2288]【POJ Challenge】生日礼物_贪心_堆的更多相关文章

  1. BZOJ3502PA2012Tanie linie&BZOJ2288[POJ Challenge]生日礼物——模拟费用流+链表+堆

    题目描述 n个数字,求不相交的总和最大的最多k个连续子序列. 1<= k<= N<= 1000000. 输入 输出 样例输入 5 2 7 -3 4 -9 5 样例输出 13   根据 ...

  2. [bzoj2288][POJ Challenge]生日礼物

    用堆维护双向链表来贪心... 数据范围显然不容许O(nm)的傻逼dp>_<..而且dp光是状态就n*m个了..显然没法优化 大概就会想到贪心乱搞了吧...一开始想贪心地通过几段小的负数把正 ...

  3. [bzoj2287][poj Challenge]消失之物_背包dp_容斥原理

    消失之物 bzoj-2287 Poj Challenge 题目大意:给定$n$个物品,第$i$个物品的权值为$W_i$.记$Count(x,i)$为第$i$个物品不允许使用的情况下拿到重量为$x$的方 ...

  4. BZOJ2288:[POJ Challenge]生日礼物——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=2288 ftiasch 18岁生日的时候,lqp18_31给她看了一个神奇的序列 A1, A2, . ...

  5. BZOJ2288:[POJ Challenge]生日礼物

    浅谈堆:https://www.cnblogs.com/AKMer/p/10284629.html 题目传送门:https://lydsy.com/JudgeOnline/problem.php?id ...

  6. [bzoj4345][POI2016]Korale_堆_贪心_线段树_dfs

    bzoj4345 POI2016 Korale 题目链接:https://lydsy.com/JudgeOnline/problem.php?id=4345 数据范围:略. 题解: 由于$k$的范围问 ...

  7. poj 3253 Fence Repair 贪心 最小堆 题解《挑战程序设计竞赛》

    地址 http://poj.org/problem?id=3253 题解 本题是<挑战程序设计>一书的例题 根据树中描述 所有切割的代价 可以形成一颗二叉树 而最后的代价总和是与子节点和深 ...

  8. [bzoj2097][Usaco2010 Dec]Exercise 奶牛健美操_贪心_树形dp_二分

    Exercise bzoj-2097 Usaco-2010 Dec 题目大意:题目链接 注释:略. 想法:题目描述生怕你不知道这题在考二分. 关键是怎么验证?我们想到贪心的删边. 这样的策略是显然正确 ...

  9. [bzoj4027][HEOI2015]兔子与樱花_贪心_树形dp

    兔子与樱花 bzoj-4027 HEOI-2015 题目大意:每个点有c[i]朵樱花,有一个称重m, son[i]+c[i]<=m.如果删除一个节点,这个节点的樱花或移动到它的祖先中深度最大的, ...

随机推荐

  1. learning gcc #pragma once

    referenc: https://zh.wikipedia.org/wiki/Pragma_once 在C和C++编程语言中,#pragma once是一个非标准但是被广泛支持的前置处理符号, 会让 ...

  2. 多线程中volatile关键字的作用

    原文链接:https://blog.csdn.net/xuwentao37x/article/details/27804169 多线程的程序是出了名的难编写.难验证.难调试.难维护,这通常是件苦差事. ...

  3. Python: 关于 sys.stdout.flush()

    stackoverflow https://stackoverflow.com/questions/10019456/usage-of-sys-stdout-flush-method Python's ...

  4. python 显示!到~的字符

    count = ): != : print(chr(i),end=" ") else: print(chr(i)) count += 输出 ! " # $ % & ...

  5. CF427D

    CF427D SA的奇技淫巧,其实就是板子. 题意: 给定两个字符串,求最短的满足各只出现一次的连续公共字串 解析: 一般情况下,SA都是用来求最长公共前缀的,好像和这道题所求的最短公共子串没有任何关 ...

  6. Java后台开发精选知识图谱

    1.引言: 学习一个新的技术时,其实不在于跟着某个教程敲出了几行.几百行代码,这样你最多只能知其然而不知其所以然,进步缓慢且深度有限,最重要的是一开始就对整个学习路线有宏观.简洁的认识,确定大的学习方 ...

  7. in和exists的区别

    表展示 首先,查询中涉及到的两个表,一个user和一个order表,具体表的内容如下: user表: order表: in 确定给定的值是否与子查询或列表中的值相匹配.in在查询的时候,首先查询子查询 ...

  8. .net 数据导出

    安装npoi,下面是具体的C#代码: public static XSSFWorkbook BuildWorkbook(DataTable dt) { var book = new XSSFWorkb ...

  9. 深度学习变革视觉计算总结(CCF-GAIR)

    孙剑博士分享的是<深度学习变革视觉计算>,分别从视觉智能.计算机摄影学和AI计算三个方面去介绍. 他首先回顾了深度学习发展历史,深度学习发展到今天并不容易,过程中遇到了两个主要障碍: 第一 ...

  10. [Java]用 MessageFormat 拼接组合字符串

    package com.hy; import java.text.MessageFormat; public class Test3 { public static void main(String[ ...