【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; }
【bzoj3638】Cf172 k-Maximum Subsequence Sum 模擬費用流+線段樹區間合並