题目链接:戳我
一类典型模型。线段树模拟建图+区间最大K段和。
因为不会写,所以参考了黄学长的博客。但是我觉得他说得不够详细,所以想好好地解释一下:
前置技能1:区间最大子段和
如果K=1的时候怎么办?大家可以去参考一下蒟蒻的这篇博客:戳我
前置技能2:最长K可重区间集问题
求最长K可重区间集的最大长度。这个是网络流能解决的问题,具体建模请看蒟蒻的这篇博客:戳我
但是我们发现如果用网络流来做的话,显然这道题的数据范围太大了,对于复杂度上届为\(O(n^2m)\)的网络流,不光说仅仅点数就有1e5个,我们连边的话肯定也至少是n*(n-1)/2。然后我们考虑一下网络流到底是怎么运作的?我们求的是最大费用流,显然不断地增广过程就是不断地在寻找费用最大的区间然后累加到答案里面。但是因为可重叠的最大量为1,所以有时候考虑到更换更优解,我们需要把流量费用退回去,也就把它变成负的。这个过程我们可以用线段树来模拟——
增广的过程我们可以用寻找最大子段和来替代。然后推流可以用线段树区间反转来做。(区间反转如果不会的话。。。或许可以看看luogu的文艺平衡树???)
然后时间复杂度就十分优秀地化简到了\(O(mklogn)\),对于四秒+很快的cf机子应该还是绰绰有余了。
备注一下代码里面的变量意义:
mx:当前值
mn:mx的相反数
嵌套——
maxs:区间最大子段和
sum:区间和(为什么要记录这个我就不说了,大家参考前置技能1)
lx:区间最大前缀子段和
rx:区间最大后缀子段和
p1:区间最大子段和的左区间
p2:区间最大子段和的右区间
因为我们合并的时候有三种情况,其中一种是有可能跨左右区间,所以我们还需要记录lp:区间最大前缀子段和的右端点,rp:区间最大后缀子段和的前端点。
代码如下:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
#define MAXN 100010
inline int read()
{
int f=1,x=0; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48); ch=getchar();}
return x*f;
}
int n,m;
int a[MAXN];
struct Node{int lp,rp,lx,rx,sum,maxs,p1,p2;};
struct Node2{int flag,tag;Node mx,mn;}node[MAXN<<2];
vector<pair<int,int> >cur;
inline int ls(int x){return x<<1;}
inline int rs(int x){return x<<1|1;}
inline Node merge(Node x,Node y)
{
Node t;
t.sum=x.sum+y.sum;
if(x.sum+y.lx>x.lx) t.lx=x.sum+y.lx,t.lp=y.lp;
else t.lx=x.lx,t.lp=x.lp;
if(x.rx+y.sum>y.rx) t.rx=x.rx+y.sum,t.rp=x.rp;
else t.rx=y.rx,t.rp=y.rp;
t.maxs=x.maxs,t.p1=x.p1,t.p2=x.p2;
if(y.maxs>t.maxs) t.maxs=y.maxs,t.p1=y.p1,t.p2=y.p2;
if(x.rx+y.lx>t.maxs) t.maxs=x.rx+y.lx,t.p1=x.rp,t.p2=y.lp;
return t;
}
inline void push_up(int x)
{
node[x].mn=merge(node[ls(x)].mn,node[rs(x)].mn);
node[x].mx=merge(node[ls(x)].mx,node[rs(x)].mx);
}
inline void init(Node &x,int pos,int k)
{
x.p1=x.p2=x.lp=x.rp=pos;
x.lx=x.rx=x.maxs=x.sum=k;
}
inline void build(int x,int l,int r)
{
if(l==r)
{
init(node[x].mn,l,-a[l]);
init(node[x].mx,l,a[l]);
return;
}
int mid=(l+r)>>1;
build(ls(x),l,mid);
build(rs(x),mid+1,r);
push_up(x);
}
inline void push_down(int x,int l,int r)
{
if(node[x].tag)
{
swap(node[ls(x)].mn,node[ls(x)].mx);
swap(node[rs(x)].mn,node[rs(x)].mx);
node[ls(x)].tag^=1,node[rs(x)].tag^=1,node[x].tag^=1;
}
}
inline void reverse(int x,int l,int r,int ll,int rr)
{
if(l==ll&&r==rr)
{swap(node[x].mn,node[x].mx);node[x].tag^=1;return;}
push_down(x,l,r);
int mid=(l+r)>>1;
if(rr<=mid) reverse(ls(x),l,mid,ll,rr);
else if(mid<ll) reverse(rs(x),mid+1,r,ll,rr);
else reverse(ls(x),l,mid,ll,mid),reverse(rs(x),mid+1,r,mid+1,rr);
push_up(x);
}
inline void update(int x,int l,int r,int pos,int k)
{
if(l==r)
{init(node[x].mx,l,k);init(node[x].mn,l,-k);return;}
int mid=(l+r)>>1;
push_down(x,l,r);
if(pos<=mid) update(ls(x),l,mid,pos,k);
else update(rs(x),mid+1,r,pos,k);
push_up(x);
}
inline Node query(int x,int l,int r,int ll,int rr)
{
if(l==ll&&r==rr) return node[x].mx;
push_down(x,l,r);
int mid=(l+r)>>1;
if(rr<=mid) return query(ls(x),l,mid,ll,rr);
else if(mid<ll) return query(rs(x),mid+1,r,ll,rr);
else return merge(query(ls(x),l,mid,ll,mid),query(rs(x),mid+1,r,mid+1,rr));
}
inline void solve(int x,int l,int r,int k)
{
int cur_ans=0;
cur.clear();
for(int i=1;i<=k;i++)
{
Node t=query(1,1,n,l,r);
if(t.maxs>0) cur_ans+=t.maxs;
else break;
reverse(1,1,n,t.p1,t.p2);
cur.push_back(make_pair(t.p1,t.p2));
}
for(int i=cur.size()-1;i>=0;i--)
reverse(1,1,n,cur[i].first,cur[i].second);
printf("%d\n",cur_ans);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
n=read();
for(int i=1;i<=n;i++) a[i]=read();
build(1,1,n);
m=read();
while(m--)
{
int op=read();
if(op==1)
{
int u=read(),v=read(),k=read();
solve(1,u,v,k);
}
else
{
int pos=read(),k=read();
update(1,1,n,pos,k);
}
}
return 0;
}