CF280D k-Maximum Subsequence Sum
一、题目
二、解法
想了好久这结论终于自己整出来了,开心。
你看这题 \(dp\) 稳超时,而且又没有什么好的贪心方法,不妨先建出网络流模型。
显然可以费用流,建 \(n+1\) 个点,相邻两个点之间连有向边,费用为 \(a_i\) 流量为 \(1\),每个点都连源汇点,然后搞个限 \(k\) 点流量的点,跑最大费用最大流即可。
但是不能直接跑网络流,因为流过一条边之后相当于反向并且费用添上负号,我们可以根据这个图总结出贪心策略:选取最大的子段,然后把这个子段负号,再继续此过程 \(k\) 次即可
用线段树维护即可,要维护最大子段和最小子段以及对应的区间,时间复杂度 \(O(mk\log n)\)
#include <cstdio>
#include <iostream>
#include <queue>
using namespace std;
const int M = 100005;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m;
struct rec
{
int l,r,c;
rec(int L=0,int R=0,int C=0) : l(L) , r(R) , c(C) {}
bool operator < (const rec &b) const
{
return c<b.c;
}
rec operator + (const rec &b) const
{
return rec(l,b.r,c+b.c);
}
};queue<rec> q;
struct node
{
rec smax,smin,lmax,lmin,rmax,rmin,sum;int fl;
node() {fl=0;}
}s[4*M];
node operator + (node x,node y)
{
node r;
r.smax=max(x.smax,y.smax);
r.smax=max(r.smax,x.rmax+y.lmax);
r.smin=min(x.smin,y.smin);
r.smin=min(r.smin,x.rmin+y.lmin);
r.lmax=max(x.lmax,x.sum+y.lmax);
r.rmax=max(y.rmax,x.rmax+y.sum);
r.lmin=min(x.lmin,x.sum+y.lmin);
r.rmin=min(y.rmin,x.rmin+y.sum);
r.sum=x.sum+y.sum;
return r;
}
void rev(int i)
{
swap(s[i].smax,s[i].smin);
swap(s[i].lmax,s[i].lmin);
swap(s[i].rmax,s[i].rmin);
s[i].smax.c*=-1;s[i].smin.c*=-1;
s[i].lmax.c*=-1;s[i].lmin.c*=-1;
s[i].rmax.c*=-1;s[i].rmin.c*=-1;
s[i].sum.c*=-1;s[i].fl^=1;
}
void down(int i)
{
if(!s[i].fl) return ;
rev(i<<1);rev(i<<1|1);
s[i].fl=0;
}
void add(int i,int l,int r,int id,int v)
{
if(l==r)
{
s[i].smax=s[i].smin=s[i].lmax=s[i].sum=
s[i].lmin=s[i].rmax=s[i].rmin=rec(l,l,v);
return ;
}
int mid=(l+r)>>1;down(i);
if(mid>=id) add(i<<1,l,mid,id,v);
else add(i<<1|1,mid+1,r,id,v);
s[i]=s[i<<1]+s[i<<1|1];
}
void upd(int i,int l,int r,int L,int R)
{
if(L>r || l>R) return ;
if(L<=l && r<=R)
{
rev(i);
return ;
}
int mid=(l+r)>>1;down(i);
upd(i<<1,l,mid,L,R);
upd(i<<1|1,mid+1,r,L,R);
s[i]=s[i<<1]+s[i<<1|1];
}
node ask(int i,int l,int r,int L,int R)
{
if(L<=l && r<=R) return s[i];
int mid=(l+r)>>1;down(i);
if(R<=mid) return ask(i<<1,l,mid,L,R);
if(L>mid) return ask(i<<1|1,mid+1,r,L,R);
return ask(i<<1,l,mid,L,R)+ask(i<<1|1,mid+1,r,L,R);
}
signed main()
{
n=read();
for(int i=1;i<=n;i++)
add(1,1,n,i,read());
m=read();
while(m--)
{
int op=read(),l=read(),r=read();
if(op==0)
{
add(1,1,n,l,r);
continue;
}
int k=read(),ans=0;
while(k--)
{
rec t=ask(1,1,n,l,r).smax;
if(t.c<=0) break;
ans+=t.c;
upd(1,1,n,t.l,t.r);
q.push(t);
}
printf("%d\n",ans);
while(!q.empty())
{
rec t=q.front();q.pop();
upd(1,1,n,t.l,t.r);
}
}
}