题目描述
Consider integer sequence a1,a2,...,an . You should run queries of two types:
- The query format is " 0 i val ". In reply to this query you should make the following assignment: ai=val .
- The query format is " 1 l r k ". In reply to this query you should print the maximum sum of at most k non-intersecting subsegments of sequence al,al+1,...,ar . Formally, you should choose at most k pairs of integers ((x1,y1),(x2,y2),...,(xt,yt) (l<=x1<=y1<x2<=y2<...<xt<=yt<=r; t<=k) such that the sum ax1+ax1+1+...+ay1+ax2+ax2+1+...+ay2+...+axt+axt+1+...+ayt is as large as possible. Note that you should choose at most k subsegments. Particularly, you can choose 0 subsegments. In this case the described sum considered equal to zero.
输入格式
The first line contains integer n(1<=n<=1e5) , 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<=1e5) — the number of queries. The next mm lines contain the queries in the format, given in the statement.
All changing queries fit into limits: 1<=i<=n , |val|<=500∣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.
题意翻译
长度为n 的数列,支持两种操作:
1.修改某个位置的值
2.询问区间[l,r] 里选出至多k 个不相交的子段和的最大值。 一共有m 个操作
感谢@Fheiwn 提供的翻译
输入输出样例
输入 #1
9 9 -8 9 -1 -1 -1 9 -8 9 3 1 1 9 1 1 1 9 2 1 4 6 3
输出 #1
17 25 0
输入 #2
15 -4 8 -3 -10 10 4 -7 -7 0 -6 3 8 -10 7 2 15 1 3 9 2 1 6 12 1 0 6 5 0 10 -7 1 4 9 1 1 7 9 1 0 10 -3 1 4 10 2 1 3 13 2 1 4 11 2 0 15 -9 0 13 -9 0 11 -10 1 5 14 2 1 6 12 1
输出 #2
14 11 15 0 15 26 18 23 8
说明/提示
In the first query of the first example you can select a single pair (1,9) . So the described sum will be 17.
Look at the second query of the first example. How to choose two subsegments? (1, 3) and (7, 9)? Definitely not, the sum we could get from (1, 3) and (7, 9) is 20, against the optimal configuration (1, 7) and (9, 9) with 25.
The answer to the third query is 0, we prefer select nothing if all of the numbers in the given interval are negative.
解题思路
首先考虑当k=1时怎么办
我们要用到的数据结构时线段树
以其中一个节点now为例,如下图,a,b分别是now的子区间
我们让线段树的每个节点维护 4 个信息
1:从左端起的最大连续和 maxl[x]
2:从右端起的最大连续和 maxl[x]
3:整个区间的最大连续和 Max[x]
4:整个区间的所有元素之和 sum[x]
如图
如图假设我们已经知道了a,b区间的信息,怎么求出now的信息呢?
很显然
sum[now]=sum[a]+sum[b]
Max[now]=max{ Max[a],Max[b] , maxr[a]+maxl[b] }//即要么是两个子区间各自的最大值,要么是穿过两个区间的最大值
同理 maxl[now]=max{ maxl[a] , sum[a]+maxl[b] }
maxr[now]=max{ maxr[b] , sum[b]+maxr[a] }
那么k!=1时怎么办呢?
需要用到模拟费用流(不过本蒟蒻并未学过)
听老师讲了半天也只听懂了做法
先找出区间最大连续值,再把这个区间取反,然后再找最大连续值,一直进行K次,把每次的和累加起来就是答案。
那么我们需要维护什么呢?(比较多,请细细看)
sum,Max,Maxl,Maxr 同上所述
因为我们要调用最大区间并取反,所以我们要知道这个区间的左右端点 maxPL,maxPR
维护这个端点需要用Maxl和Maxr的端点 maxpl,maxpr
取反时的懒标记lazy
当我们取反时,最大会变成最小,最小会变成最大,因此我们还需要维护上述和元素相反的最小值
因此需要维护
L,R,sum,Max,maxPL,maxPR,Min,minPL,minPR,Maxl,maxpl,Minl,minpl,Maxr,maxpr,Minr,minpr,lazy
(真的有点多)
合并过程也很好理解了,如下图
inline Node merge(const Node &a, const Node &b)
{
Node New;
New.L=a.L;
New.R=b.R;
New.sum=a.sum+b.sum;
if(a.Maxl>=a.sum+b.Maxl)
New.Maxl=a.Maxl,New.maxpl=a.maxpl;
else New.Maxl=a.sum+b.Maxl,New.maxpl=b.maxpl;
if(a.Minl<=a.sum+b.Minl)
New.Minl=a.Minl,New.minpl=a.minpl;
else New.Minl=a.sum+b.Minl,New.minpl=b.minpl;
if(b.Maxr>=b.sum+a.Maxr)
New.Maxr=b.Maxr,New.maxpr=b.maxpr;
else New.Maxr=b.sum+a.Maxr,New.maxpr=a.maxpr;
if(b.Minr<=b.sum+a.Minr)
New.Minr=b.Minr,New.minpr=b.minpr;
else New.Minr=b.sum+a.Minr,New.minpr=a.minpr;
New.Max=a.Maxr+b.Maxl;
New.maxPL=a.maxpr;
New.maxPR=b.maxpl;
if(New.Max<a.Max)
New.Max=a.Max,New.maxPL=a.maxPL,New.maxPR=a.maxPR;
if(New.Max<b.Max)
New.Max=b.Max,New.maxPL=b.maxPL,New.maxPR=b.maxPR;
New.Min=a.Minr+b.Minl;New.minPL=a.minpr;New.minPR=b.minpl;
if(New.Min>a.Min)
New.Min=a.Min,New.minPL=a.minPL,New.minPR=a.minPR;
if(New.Min>b.Min)
New.Min=b.Min,New.minPL=b.minPL,New.minPR=b.minPR;
New.lazy=0;
return New;
}
反转过程也很好理解了,就是先把上述元素取反,在把最大值和最小值交换
inline void Rev(Node &a)
{
a.lazy^=1;
a.sum=-a.sum;
swap(a.Maxl,a.Minl);
swap(a.maxpl,a.minpl);
a.Maxl=-a.Maxl;
a.Minl=-a.Minl;
swap(a.Maxr,a.Minr);
swap(a.maxpr,a.minpr);
a.Maxr=-a.Maxr;
a.Minr=-a.Minr;
swap(a.maxPL,a.minPL);
swap(a.maxPR,a.minPR);
a.Max=-a.Max;
a.Min=-a.Min;
swap(a.Max,a.Min);
}
初始化的时候要记得把所有代码都初始化
inline void build(int k,int l,int r)
{
if(l==r)
{
tree[k].L=tree[k].R=l;
tree[k].maxPL=tree[k].maxPR=l;
tree[k].minPL=tree[k].minPR=l;
tree[k].maxpl=tree[k].maxpr=l;
tree[k].minpl=tree[k].minpr=l;
tree[k].sum=tree[k].Max=tree[k].Min=a[l];
tree[k].Maxl=tree[k].Maxr=a[l];
tree[k].Minl=tree[k].Minr=a[l];
tree[k].lazy=0;
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
pushup(k);
}
还有其他一些细节,可以借助代码理解一下
完整代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+7;
struct Node
{
int L,R,sum,Max,maxPL,maxPR,Min,minPL,minPR,Maxl,maxpl,Minl,minpl,Maxr,maxpr,Minr,minpr;
bool lazy;
}tree[4*N],op[22];
inline Node merge(const Node &a, const Node &b)
{
Node New;
New.L=a.L;
New.R=b.R;
New.sum=a.sum+b.sum;
if(a.Maxl>=a.sum+b.Maxl)
New.Maxl=a.Maxl,New.maxpl=a.maxpl;
else New.Maxl=a.sum+b.Maxl,New.maxpl=b.maxpl;
if(a.Minl<=a.sum+b.Minl)
New.Minl=a.Minl,New.minpl=a.minpl;
else New.Minl=a.sum+b.Minl,New.minpl=b.minpl;
if(b.Maxr>=b.sum+a.Maxr)
New.Maxr=b.Maxr,New.maxpr=b.maxpr;
else New.Maxr=b.sum+a.Maxr,New.maxpr=a.maxpr;
if(b.Minr<=b.sum+a.Minr)
New.Minr=b.Minr,New.minpr=b.minpr;
else New.Minr=b.sum+a.Minr,New.minpr=a.minpr;
New.Max=a.Maxr+b.Maxl;
New.maxPL=a.maxpr;
New.maxPR=b.maxpl;
if(New.Max<a.Max)
New.Max=a.Max,New.maxPL=a.maxPL,New.maxPR=a.maxPR;
if(New.Max<b.Max)
New.Max=b.Max,New.maxPL=b.maxPL,New.maxPR=b.maxPR;
New.Min=a.Minr+b.Minl;New.minPL=a.minpr;New.minPR=b.minpl;
if(New.Min>a.Min)
New.Min=a.Min,New.minPL=a.minPL,New.minPR=a.minPR;
if(New.Min>b.Min)
New.Min=b.Min,New.minPL=b.minPL,New.minPR=b.minPR;
New.lazy=0;
return New;
}
inline void pushup(int k)
{
tree[k]=merge(tree[k<<1],tree[k<<1|1]);
}
inline void Rev(Node &a)
{
a.lazy^=1;
a.sum=-a.sum;
swap(a.Maxl,a.Minl);
swap(a.maxpl,a.minpl);
a.Maxl=-a.Maxl;
a.Minl=-a.Minl;
swap(a.Maxr,a.Minr);
swap(a.maxpr,a.minpr);
a.Maxr=-a.Maxr;
a.Minr=-a.Minr;
swap(a.maxPL,a.minPL);
swap(a.maxPR,a.minPR);
a.Max=-a.Max;
a.Min=-a.Min;
swap(a.Max,a.Min);
}
inline void pushdown(int k)
{
if(tree[k].lazy)
{
Rev(tree[k<<1]);
Rev(tree[k<<1|1]);
tree[k].lazy=0;
}
}
int n,m,a[N];
inline void build(int k,int l,int r)
{
if(l==r)
{
tree[k].L=tree[k].R=l;
tree[k].maxPL=tree[k].maxPR=l;
tree[k].minPL=tree[k].minPR=l;
tree[k].maxpl=tree[k].maxpr=l;
tree[k].minpl=tree[k].minpr=l;
tree[k].sum=tree[k].Max=tree[k].Min=a[l];
tree[k].Maxl=tree[k].Maxr=a[l];
tree[k].Minl=tree[k].Minr=a[l];
tree[k].lazy=0;
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
pushup(k);
}
inline void update(int k,int l,int r,int x,int v)
{
if(l==r)
{
tree[k].L=tree[k].R=l;
tree[k].maxPL=tree[k].maxPR=l;
tree[k].minPL=tree[k].minPR=l;
tree[k].maxpl=tree[k].maxpr=l;
tree[k].minpl=tree[k].minpr=l;
tree[k].sum=tree[k].Max=tree[k].Min=v;
tree[k].Maxl=tree[k].Maxr=v;
tree[k].Minl=tree[k].Minr=v;
tree[k].lazy=0;
return;
}
pushdown(k);
int mid=(l+r)>>1;
if(x<=mid) update(k<<1,l,mid,x,v);
else update(k<<1|1,mid+1,r,x,v);
pushup(k);
}
inline void Reverse(int k,int l,int r,int L,int R)
{
if(L<=l&&r<=R)
{
Rev(tree[k]);
return;
}
pushdown(k);
int mid=(l+r)>>1;
if(L<=mid) Reverse(k<<1,l,mid,L,R);
if(mid<R) Reverse(k<<1|1,mid+1,r,L,R);
pushup(k);
}
inline Node query(int k,int l,int r,int L,int R)
{
if(L<=l&&r<=R) return tree[k];
pushdown(k);
int mid=(l+r)>>1;
if(R<=mid) return query(k<<1,l,mid,L,R);
else if(mid<L) return query(k<<1|1,mid+1,r,L,R);
else return merge(query(k<<1,l,mid,L,R),query(k<<1|1,mid+1,r,L,R));
}
int read()
{
int x=0,f=0;char ch=0;
while(!isdigit(ch)){f|=ch=='-';ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return f?-x:x;
}
int main()
{
freopen("kmaximumsubsequencesum.in","r",stdin);
freopen("kmaximumsubsequencesum.out","w",stdout);
n=read();
for(int i=1;i<=n;i++)
a[i]=read();
build(1,1,n);
m=read();
int opt,l,r,k,ans,t;
while(m--)
{
opt=read();
l=read();
r=read();
if(opt==0)
{
update(1,1,n,l,r);
}
else
{
k=read();
ans=0;t=k;
for(int i=1;i<=k;i++)
{
op[i]=query(1,1,n,l,r);
if(op[i].Max<=0)
{
t=i-1;
break;
}
Reverse(1,1,n,op[i].maxPL,op[i].maxPR);
ans+=op[i].Max;
}
for(int i=1;i<=t;i++)
Reverse(1,1,n,op[i].maxPL,op[i].maxPR);//取反之后记得再返回来
printf("%d\n",ans);
}
}
}