题目描述

给一列数,要求支持操作: 1.修改某个数的值 2.读入l,r,k,询问在[l,r]内选不相交的不超过k个子段,最大的和是多少。

输入

The first line contains integer n (1 ≤ n ≤ 105), showing how many numbers the sequence has. The next line contains n integers a1, a2, ..., an (|ai| ≤ 500).

The third line contains integer m (1 ≤ m ≤ 105) — the number of queries. The next m lines contain the queries in the format, given in the statement.

All changing queries fit into limits: 1 ≤ i ≤ n, |val| ≤ 500.

All queries to count the maximum sum of at most k non-intersecting subsegments fit into limits: 1 ≤ l ≤ r ≤ n, 1 ≤ k ≤ 20. It is guaranteed that the number of the queries to count the maximum sum of at most k non-intersecting subsegments doesn't exceed 10000.

输出

For each query to count the maximum sum of at most k non-intersecting subsegments print the reply — the maximum sum. Print the answers to the queries in the order, in which the queries follow in the input.

样例输入

9
9 -8 9 -1 -1 -1 9 -8 9
3
1 1 9 1
1 1 9 2
1 4 6 3

样例输出

17
25
0


题解

模拟费用流+线段树区间合并

一开始想了个类似于dp的线段树区间合并结果一看数据范围果断放弃了。。。看了题解才知道是模拟费用流。。。

考虑如果用费用流的话怎么处理:每个点有一个大小为点权的费用,每次选择一段区间,获得这些点权和的费用,然后反向边使得它们的费用取相反数。

这个过程需要维护最大连续子段和(及其位置)、支持区间翻转。可以使用线段树来维护。

每个节点维护这段区间的区间和,包含左端点的最大连续子段和、包含右端点的最大连续子段和、整体的最大连续子段和,以及最小连续字段和;还要维护翻转标记。同时,对于和及连续字段和还要维护出现的区间位置。

每个询问不断的找区间内最大连续子段和,如果其大于0则取出并区间取相反数(模拟增广的过程)。最后再把这些取了相反数的区间还原回来。

代码量极大。。。强烈建议使用结构体重载运算符以减少代码量。

时间复杂度$O(nk\log n)$。

  1. #include <cstdio>
  2. #include <algorithm>
  3. #define N 100010
  4. #define lson l , mid , x << 1
  5. #define rson mid + 1 , r , x << 1 | 1
  6. using namespace std;
  7. struct data
  8. {
  9. int v , l , r;
  10. data() {}
  11. data(int V , int L , int R) {v = V , l = L , r = R;}
  12. bool operator<(const data &a)const {return v < a.v;}
  13. data operator+(const data &a)const {return data(v + a.v , l , a.r);}
  14. data operator-()const {return data(-v , l , r);}
  15. }now , sta[25];
  16. struct seg
  17. {
  18. data vsum , lmax , lmin , rmax , rmin , tmax , tmin;
  19. int rev;
  20. seg() {}
  21. seg(int v , int p)
  22. {
  23. vsum = data(v , p , p) , rev = 0;
  24. if(v > 0)
  25. {
  26. lmax = rmax = tmax = data(v , p , p);
  27. lmin = data(0 , p , p - 1) , rmin = data(0 , p + 1 , p) , tmin = data(0 , 0 , 0);
  28. }
  29. else
  30. {
  31. lmax = data(0 , p , p - 1) , rmax = data(0 , p + 1 , p) , tmax = data(0 , 0 , 0);
  32. lmin = rmin = tmin = data(v , p , p);
  33. }
  34. }
  35. seg operator+(const seg &a)const
  36. {
  37. seg ans;
  38. ans.vsum = vsum + a.vsum;
  39. ans.lmax = max(lmax , vsum + a.lmax) , ans.lmin = min(lmin , vsum + a.lmin);
  40. ans.rmax = max(a.rmax , rmax + a.vsum) , ans.rmin = min(a.rmin , rmin + a.vsum);
  41. ans.tmax = max(rmax + a.lmax , max(tmax , a.tmax)) , ans.tmin = min(rmin + a.lmin , min(tmin , a.tmin));
  42. ans.rev = 0;
  43. return ans;
  44. }
  45. }a[N << 2];
  46. inline void pushup(int x)
  47. {
  48. a[x] = a[x << 1] + a[x << 1 | 1];
  49. }
  50. inline void rever(int x)
  51. {
  52. swap(a[x].lmax , a[x].lmin) , swap(a[x].rmax , a[x].rmin) , swap(a[x].tmax , a[x].tmin);
  53. a[x].vsum = -a[x].vsum;
  54. a[x].lmax = -a[x].lmax , a[x].lmin = -a[x].lmin;
  55. a[x].rmax = -a[x].rmax , a[x].rmin = -a[x].rmin;
  56. a[x].tmax = -a[x].tmax , a[x].tmin = -a[x].tmin;
  57. a[x].rev ^= 1;
  58. }
  59. inline void pushdown(int x)
  60. {
  61. if(a[x].rev) rever(x << 1) , rever(x << 1 | 1) , a[x].rev = 0;
  62. }
  63. void build(int l , int r , int x)
  64. {
  65. if(l == r)
  66. {
  67. int v;
  68. scanf("%d" , &v) , a[x] = seg(v , l);
  69. return;
  70. }
  71. int mid = (l + r) >> 1;
  72. build(lson) , build(rson);
  73. pushup(x);
  74. }
  75. void modify(int p , int v , int l , int r , int x)
  76. {
  77. if(l == r)
  78. {
  79. a[x] = seg(v , l);
  80. return;
  81. }
  82. pushdown(x);
  83. int mid = (l + r) >> 1;
  84. if(p <= mid) modify(p , v , lson);
  85. else modify(p , v , rson);
  86. pushup(x);
  87. }
  88. void update(int b , int e , int l , int r , int x)
  89. {
  90. if(b <= l && r <= e)
  91. {
  92. rever(x);
  93. return;
  94. }
  95. pushdown(x);
  96. int mid = (l + r) >> 1;
  97. if(b <= mid) update(b , e , lson);
  98. if(e > mid) update(b , e , rson);
  99. pushup(x);
  100. }
  101. seg query(int b , int e , int l , int r , int x)
  102. {
  103. if(b <= l && r <= e) return a[x];
  104. pushdown(x);
  105. int mid = (l + r) >> 1;
  106. if(e <= mid) return query(b , e , lson);
  107. else if(b > mid) return query(b , e , rson);
  108. else return query(b , e , lson) + query(b , e , rson);
  109. }
  110. int main()
  111. {
  112. int n , m , opt , x , y , z , tot , ans;
  113. scanf("%d" , &n);
  114. build(1 , n , 1);
  115. scanf("%d" , &m);
  116. while(m -- )
  117. {
  118. scanf("%d%d%d" , &opt , &x , &y);
  119. if(opt == 1)
  120. {
  121. scanf("%d" , &z) , tot = ans = 0;
  122. while(tot < z)
  123. {
  124. now = query(x , y , 1 , n , 1).tmax;
  125. if(now.v <= 0) break;
  126. ans += now.v , update(now.l , now.r , 1 , n , 1);
  127. sta[++tot] = now;
  128. }
  129. printf("%d\n" , ans);
  130. while(tot) update(sta[tot].l , sta[tot].r , 1 , n , 1) , tot -- ;
  131. }
  132. else modify(x , y , 1 , n , 1);
  133. }
  134. return 0;
  135. }

【bzoj3638】Cf172 k-Maximum Subsequence Sum 模拟费用流+线段树区间合并的更多相关文章

  1. BZOJ.3638.CF172 k-Maximum Subsequence Sum(模拟费用流 线段树)

    题目链接 各种zz错误..简直了 /* 19604kb 36292ms 题意:选$k$段不相交的区间,使其权值和最大. 朴素线段树:线段树上每个点维护O(k)个信息,区间合并时O(k^2),总O(mk ...

  2. Codeforces 280D k-Maximum Subsequence Sum [模拟费用流,线段树]

    洛谷 Codeforces bzoj1,bzoj2 这可真是一道n倍经验题呢-- 思路 我首先想到了DP,然后矩阵,然后线段树,然后T飞-- 搜了题解之后发现是模拟费用流. 直接维护选k个子段时的最优 ...

  3. BZOJ 3836 Codeforces 280D k-Maximum Subsequence Sum (模拟费用流、线段树)

    题目链接 (BZOJ) https://www.lydsy.com/JudgeOnline/problem.php?id=3836 (Codeforces) http://codeforces.com ...

  4. BZOJ3638[Codeforces280D]k-Maximum Subsequence Sum&BZOJ3272Zgg吃东西&BZOJ3267KC采花——模拟费用流+线段树

    题目描述 给一列数,要求支持操作: 1.修改某个数的值 2.读入l,r,k,询问在[l,r]内选不相交的不超过k个子段,最大的和是多少. 输入 The first line contains inte ...

  5. CF280D-k-Maximum Subsequence Sum【模拟费用流,线段树】

    正题 题目链接:https://www.luogu.com.cn/problem/CF280D 题目大意 一个长度为\(n\)的序列,\(m\)次操作 修改一个数 询问一个区间中选出\(k\)段不交子 ...

  6. BZOJ2040[2009国家集训队]拯救Protoss的故乡——模拟费用流+线段树+树链剖分

    题目描述 在星历2012年,星灵英雄Zeratul预测到他所在的Aiur行星在M天后会发生持续性暴雨灾害,尤其是他们的首都.而Zeratul作为星灵族的英雄,当然是要尽自己最大的努力帮助星灵族渡过这场 ...

  7. 【BZOJ3638】Cf172 k-Maximum Subsequence Sum 线段树区间合并(模拟费用流)

    [BZOJ3638]Cf172 k-Maximum Subsequence Sum Description 给一列数,要求支持操作: 1.修改某个数的值 2.读入l,r,k,询问在[l,r]内选不相交 ...

  8. HDU5107---K-short Problem (线段树区间 合并、第k大)

    题意:二维平面上 N 个高度为 Hi 建筑物,M次询问,每次询问输出 位于坐标(x ,y)左下角(也就是xi <= x && yi <= y)的建筑物中的第k高的建筑物的高 ...

  9. BZOJ 4276 [ONTAK2015]Bajtman i Okrągły Robin 费用流+线段树优化建图

    Description 有n个强盗,其中第i个强盗会在[a[i],a[i]+1],[a[i]+1,a[i]+2],...,[b[i]-1,b[i]]这么多段长度为1时间中选出一个时间进行抢劫,并计划抢 ...

随机推荐

  1. 使用Git操作码云

    一.安装并配置 .安装git 下载地址: 官方网站:https://git-for-windows.github.io/ 国内镜像:https://pan.baidu.com/s/1kU5OCOB#l ...

  2. margin与padding大比拼

    用margin还是用padding这个问题相信是每个学css的人都想要去深入了解的. CSS边距属性定义元素周围的空间.通过使用单独的属性,可以对上.右.下.左的外边距进行设置.也可以使用简写的外边距 ...

  3. DNS的主从,转发与负载功能

    接着原来<DNS原理与应用>的文章,本章内容主要通过实现DNS的主从,转发,及基于域名解析不同的ip实现后端服务负载均衡的效果.最后再实现DNS的高级功能:类似CDN原理实现基于IP实现区 ...

  4. kubernetes基础架构及原理

    kubernetes简称“k8s” 其中“8”代表的是“k”和“s”中间的8个字母. k8s是Google公司开发的Borg项目中独立出来的容器编排工具,然后将其捐献给CNCF这个组织,然后发扬光大. ...

  5. Sublime Text3的快捷键和插件

    今天重装了一下Sublime Text3,发现了一个不错的网站,关于Sublime Text3的插件安装介绍的很详细,还有右键增强菜单和浏览器打开快捷键的创建.奉上链接 http://www.cnbl ...

  6. scrapy--Cookies

    大家好,之前看到的关于cookies的应用,由于有段时间没看,再看的时候花了一些时间,来给大家总结下.本文是根据:"http://www.bubuko.com/infodetail-2233 ...

  7. Python中的文件和目录操作实现

    Python中的文件和目录操作实现 对于文件和目录的处理,虽然可以通过操作系统命令来完成,但是Python语言为了便于开发人员以编程的方式处理相关工作,提供了许多处理文件和目录的内置函数.重要的是,这 ...

  8. 创建数据库配置文件ini(转)

    一.有必要了解INI文件的结构: ;注释 [小节名] 关键字=值 ... ---- INI文件允许有多个小节,每个小节又允许有多个关键字, “=”后面是该关键字的值. ---- 值的类型有三种:字符串 ...

  9. Hive学习路线图

  10. PTA 7-10(图) 旅游规划 最短路问题

    7-10(图) 旅游规划 (25 分) 有了一张自驾旅游路线图,你会知道城市间的高速公路长度.以及该公路要收取的过路费.现在需要你写一个程序,帮助前来咨询的游客找一条出发地和目的地之间的最短路径.如果 ...