BZOJ 3638 k-Maximum Subsequence Sum

来源:互联网 发布:st6015塔吊相关数据? 编辑:程序博客网 时间:2024/05/20 13:03

题目大意:给一列数,要求支持操作: 1.修改某个数的值 2.读入l,r,k,询问在[l,r]内选不相交的不超过k个子段,最大的和是多少。

如果仅仅是询问一次,我们可以构图费用流,跑一次就行了。但题目是多组询问,直接费用流显然会TLE。自然地,我们想到对残余网络进行增广,但每次地询问区间不同,而且有修改操作,这种做法也不可行。本蒟蒻膜拜了一下hzwer大大的题解,发现根据费用流每次求最长路的性质,可以手动增广,也就是每次用线段树求一个最大子段和,然后将整个区间取反,继续增广,直到和为0或者个数达到K次为止。(可以这么理解,增广之后正向弧达到满流,反向弧流减少,而反向弧中费用为负值)接下来就是线段树的基本操作了。我们要维护区间最大和以及位置(正向弧和反向弧的信息都要维护),还要维护一个flag记录上次增广的是哪条弧,但只要想清楚还是很好写的。

#include<iostream>#include<algorithm>#include<cstdio>#include<cstring>#include<vector>using namespace std;const int maxn=200000+10;struct node{  int lx,lp,rx,rp,sum;  int mx,p1,p2;};struct data{  node mx,mn;  int flag;}t[maxn*4];struct arr{  int l,r;};vector<arr> h;int n,m,op,a[maxn],cc;void init(node &p,int l,int v){  p.lp=p.rp=p.p1=p.p2=l;  p.mx=p.lx=p.rx=p.sum=v;}node merge(node a,node b){  node res;res.sum=a.sum+b.sum;  res.lx=a.lx;res.lp=a.lp;  if(a.sum+b.lx>res.lx) res.lx=a.sum+b.lx,res.lp=b.lp;  res.rx=b.rx;res.rp=b.rp;  if(b.sum+a.rx>res.rx) res.rx=b.sum+a.rx,res.rp=a.rp;  res.mx=a.mx;res.p1=a.p1;res.p2=a.p2;  if(b.mx>res.mx) res.mx=b.mx,res.p2=b.p2,res.p1=b.p1;  if(a.rx+b.lx>res.mx) res.mx=a.rx+b.lx,res.p1=a.rp,res.p2=b.lp;  return res;}void pushup(int p){  t[p].mx=merge(t[p*2].mx,t[p*2+1].mx);  t[p].mn=merge(t[p*2].mn,t[p*2+1].mn);}void pushdown(int p){  if(t[p].flag)  {    swap(t[p*2].mn,t[p*2].mx);    swap(t[p*2+1].mn,t[p*2+1].mx);    t[p].flag^=1;t[p*2].flag^=1;t[p*2+1].flag^=1;  }}void build(int p,int l,int r){  if(l==r)  {    init(t[p].mx,l,a[l]);    init(t[p].mn,l,-a[l]);    return;  }  int mid=(l+r)>>1;  build(p*2,l,mid);build(p*2+1,mid+1,r);  pushup(p);}void update(int p,int l,int r,int x,int v){  if(x<l||x>r) return;  if(l==r)  {    init(t[p].mx,l,v);    init(t[p].mn,l,-v);    return;  }  pushdown(p);  int mid=(l+r)>>1;  update(p*2,l,mid,x,v);  update(p*2+1,mid+1,r,x,v);  pushup(p);}void reverse(int p,int l,int r,int x,int y){  if(l>y||r<x) return;  if(l>=x&&r<=y)  {    swap(t[p].mn,t[p].mx);    t[p].flag^=1;    return;  }  pushdown(p);int mid=(l+r)>>1;  reverse(p*2,l,mid,x,y);  reverse(p*2+1,mid+1,r,x,y);  pushup(p);}node query(int p,int l,int r,int x,int y){  if(l>=x&&r<=y)   {    return t[p].mx;  }  int mid=(l+r)>>1;  pushdown(p);  if(y<=mid) return query(p*2,l,mid,x,y);  else if(x>mid) return query(p*2+1,mid+1,r,x,y);  else return merge(query(p*2,l,mid,x,y),query(p*2+1,mid+1,r,x,y));}void solve(int l,int r,int k){  int res=0;h.clear();  for(int i=1;i<=k;i++)  {    node hm=query(1,1,n,l,r);    if(hm.mx<=0) break;    else res+=hm.mx;    reverse(1,1,n,hm.p1,hm.p2);    h.push_back((arr){hm.p1,hm.p2});    //cout<<hm.p1<<' '<<hm.p2<<endl;  }  for(int i=h.size()-1;i>=0;i--)    reverse(1,1,n,h[i].l,h[i].r);  printf("%d\n",res);}int main(){  //freopen("3638.in","r",stdin);  //freopen("3638.out","w",stdout);  scanf("%d",&n);  for(int i=1;i<=n;i++) scanf("%d",&a[i]);  build(1,1,n);int op,l,r,k;  scanf("%d",&m);  for(int i=1;i<=m;i++)  {    cc++;    scanf("%d%d%d",&op,&l,&r);    if(op!=1) update(1,1,n,l,r),a[l]=r;    else    {      scanf("%d",&k);      solve(l,r,k);    }  }  return 0;}  
0 0