【bzoj3638】Cf172 k-Maximum Subsequence Sum 模拟费用流+线段树区间合并

题目描述

给一列数,要求支持操作: 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)$。

#include <cstdio>
#include <algorithm>
#define N 100010
#define lson l , mid , x << 1
#define rson mid + 1 , r , x << 1 | 1
using namespace std;
struct data
{
	int v , l , r;
	data() {}
	data(int V , int L , int R) {v = V , l = L , r = R;}
	bool operator<(const data &a)const {return v < a.v;}
	data operator+(const data &a)const {return data(v + a.v , l , a.r);}
	data operator-()const {return data(-v , l , r);}
}now , sta[25];
struct seg
{
	data vsum , lmax , lmin , rmax , rmin , tmax , tmin;
	int rev;
	seg() {}
	seg(int v , int p)
	{
		vsum = data(v , p , p) , rev = 0;
		if(v > 0)
		{
			lmax = rmax = tmax = data(v , p , p);
			lmin = data(0 , p , p - 1) , rmin = data(0 , p + 1 , p) , tmin = data(0 , 0 , 0);
		}
		else
		{
			lmax = data(0 , p , p - 1) , rmax = data(0 , p + 1 , p) , tmax = data(0 , 0 , 0);
			lmin = rmin = tmin = data(v , p , p);
		}
	}
	seg operator+(const seg &a)const
	{
		seg ans;
		ans.vsum = vsum + a.vsum;
		ans.lmax = max(lmax , vsum + a.lmax) , ans.lmin = min(lmin , vsum + a.lmin);
		ans.rmax = max(a.rmax , rmax + a.vsum) , ans.rmin = min(a.rmin , rmin + a.vsum);
		ans.tmax = max(rmax + a.lmax , max(tmax , a.tmax)) , ans.tmin = min(rmin + a.lmin , min(tmin , a.tmin));
		ans.rev = 0;
		return ans;
	}
}a[N << 2];
inline void pushup(int x)
{
	a[x] = a[x << 1] + a[x << 1 | 1];
}
inline void rever(int x)
{
	swap(a[x].lmax , a[x].lmin) , swap(a[x].rmax , a[x].rmin) , swap(a[x].tmax , a[x].tmin);
	a[x].vsum = -a[x].vsum;
	a[x].lmax = -a[x].lmax , a[x].lmin = -a[x].lmin;
	a[x].rmax = -a[x].rmax , a[x].rmin = -a[x].rmin;
	a[x].tmax = -a[x].tmax , a[x].tmin = -a[x].tmin;
	a[x].rev ^= 1;
}
inline void pushdown(int x)
{
	if(a[x].rev) rever(x << 1) , rever(x << 1 | 1) , a[x].rev = 0;
}
void build(int l , int r , int x)
{
	if(l == r)
	{
		int v;
		scanf("%d" , &v) , a[x] = seg(v , l);
		return;
	}
	int mid = (l + r) >> 1;
	build(lson) , build(rson);
	pushup(x);
}
void modify(int p , int v , int l , int r , int x)
{
	if(l == r)
	{
		a[x] = seg(v , l);
		return;
	}
	pushdown(x);
	int mid = (l + r) >> 1;
	if(p <= mid) modify(p , v , lson);
	else modify(p , v , rson);
	pushup(x);
}
void update(int b , int e , int l , int r , int x)
{
	if(b <= l && r <= e)
	{
		rever(x);
		return;
	}
	pushdown(x);
	int mid = (l + r) >> 1;
	if(b <= mid) update(b , e , lson);
	if(e > mid) update(b , e , rson);
	pushup(x);
}
seg query(int b , int e , int l , int r , int x)
{
	if(b <= l && r <= e) return a[x];
	pushdown(x);
	int mid = (l + r) >> 1;
	if(e <= mid) return query(b , e , lson);
	else if(b > mid) return query(b , e , rson);
	else return query(b , e , lson) + query(b , e , rson);
}
int main()
{
	int n , m , opt , x , y , z , tot , ans;
	scanf("%d" , &n);
	build(1 , n , 1);
	scanf("%d" , &m);
	while(m -- )
	{
		scanf("%d%d%d" , &opt , &x , &y);
		if(opt == 1)
		{
			scanf("%d" , &z) , tot = ans = 0;
			while(tot < z)
			{
				now = query(x , y , 1 , n , 1).tmax;
				if(now.v <= 0) break;
				ans += now.v , update(now.l , now.r , 1 , n , 1);
				sta[++tot] = now;
			}
			printf("%d\n" , ans);
			while(tot) update(sta[tot].l , sta[tot].r , 1 , n , 1) , tot -- ;
		}
		else modify(x , y , 1 , n , 1);
	}
	return 0;
}
时间: 2024-10-08 20:43:36

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

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

题目链接 各种zz错误..简直要写疯 /* 19604kb 36292ms 朴素线段树:线段树上每个点维护O(k)个信息,区间合并时O(k^2),总O(mk^2logn)->GG 考虑费用流:建一条n+1个点的链(点权设在边上,故需n+1个点),链上每个点和S.T连边,相邻点连边 这样数列中的区间和每条增广路一一对应 每次最多增广k次,O(nmk)->still GG 考虑费用流这一过程的实质:每次增广相当于贪心,本质上只有两种情况: 选取一段(新增一个区间).从已选的某个区间中删除一段 使用

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

[BZOJ3638]Cf172 k-Maximum Subsequence Sum Description 给一列数,要求支持操作: 1.修改某个数的值 2.读入l,r,k,询问在[l,r]内选不相交的不超过k个子段,最大的和是多少.1 ≤ n ≤ 105,1 ≤ m ≤ 105,1 ≤ l ≤ r ≤ n, 1 ≤ k ≤ 20 Sample Input 9 9 -8 9 -1 -1 -1 9 -8 9 3 1 1 9 1 1 1 9 2 1 4 6 3 Sample Output 17 25

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

题意:二维平面上 N 个高度为 Hi 建筑物,M次询问,每次询问输出 位于坐标(x ,y)左下角(也就是xi <= x && yi <= y)的建筑物中的第k高的建筑物的高度,如果不存在输出-1. 思路:可以发现k很小,最大才是10.对于区间第k大的问题,如果k很小的话,线段树也是可以的,,当然这里要用到 区间合并,对于每个节点 记录其 孩子中 前2*k个高度(不一定非要2*k,只要大于k就可以,至于为什么,自己可以想想),进行合并排序. 1 #include <cstd

Educational Codeforces Round 72 (Rated for Div. 2)E. Sum Queries?(线段树区间合并)

https://codeforc.es/contest/1217/problem/E 建立9棵数位线段树维护区间最小值和次小值,建议用struct建树方便进行区间合并 1 #define bug(x) cout<<#x<<" is "<<x<<endl 2 #define IO std::ios::sync_with_stdio(0) 3 #include <bits/stdc++.h> 4 #define iter ::it

1007 Maximum Subsequence Sum (25分) 求最大连续区间和

1007 Maximum Subsequence Sum (25分) Given a sequence of K integers { N?1??, N?2??, ..., N?K?? }. A continuous subsequence is defined to be { N?i??, N?i+1??, ..., N?j?? } where 1≤i≤j≤K. The Maximum Subsequence is the continuous subsequence which has th

Maximum Subsequence Sum - 最大子列和问题_C语言实现

第一次写这方面的blog.自己也是初次接触相关知识,写的有不妥的地方十分欢迎大家指正~ 这是浙大PAT上的一道算法题(据说是浙大04年研究生复试题),题目是这样的: Maximum Subsequence Sum Given a sequence of KK integers { N_1N?1??, N_2N?2??, ..., N_KN?K?? }. A continuous subsequence is defined to be { N_iN?i??, N_{i+1}N?i+1??, ..

Maximum Subsequence Sum 最大子序列和的进击之路

本文解决最大子序列和问题,有两个题目组成,第二个题目比第一个要求多一些(其实就是要求输出子序列首尾元素). 01-复杂度1 最大子列和问题   (20分) 给定KK个整数组成的序列{ N1??, N2??, ..., NK?? },"连续子列"被定义为{ N?i??, Ni+1 ..., Nj },其中 1≤i≤j≤K."最大子列和"则被定义为所有连续子列元素的和中最大者.例如给定序列{ -2, 11, -4, 13, -5, -2 },其连续子列{ 11, -4,

pat1007. Maximum Subsequence Sum (25)

1007. Maximum Subsequence Sum (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue Given a sequence of K integers { N1, N2, ..., NK }. A continuous subsequence is defined to be { Ni, Ni+1, ..., Nj } where 1 <= i <= j <= K. The

1007. Maximum Subsequence Sum (25)——PAT (Advanced Level) Practise

题目信息: 1007. Maximum Subsequence Sum (25) 时间限制 400 ms 内存限制 32000 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue Given a sequence of K integers { N1, N2, ..., NK }. A continuous subsequence is defined to be { Ni, Ni+1, ..., Nj } where 1 <= i <= j <=