CF280D k-Maximum Subsequence Sum 線段樹
阿新 • • 發佈:2019-02-13
題目大意:
給你一個長度為的序列,支援兩種操作:
1.修改某個位置的值。
2.在區間裡選擇不超過個不相交的子區間和的最大值。
運算元。
分析:
因為比較小,可以考慮使用費用流的模型。源點向每個點連一條流量為,費用為的邊;向連一條費用為,流量為的邊;向匯點連流量為,費用為的邊。那麼,一滴從的流,代表選取區間。可以發現的是,每多流一滴流量,就多一個區間。問題就變成了使用不多於滴流量,能得到的最大費用。因為每次增廣只會多一點流量,而每次要查詢的最長路就是每次增廣的貢獻,這個貢獻是由一段區間構成。
所以,每次增廣相當於找到詢問區間的最大子區間和,然後再把該區間取反(反向弧的費用是正向弧的相反數)。舉個例子,如果第一次選了區間,然後取反後取了區間,此時相當於取了區間和。增廣完次,或者增廣貢獻小於時,就得到了答案。
因為不可能同時選共起點或共終點的區間,假如我們第一次取了 ,那麼一定不可能取反第二次後會取,這樣得到的區間是,因為我們保證每次增廣的貢獻大於等於,這個區間是比大的,與一開始就取最大子區間矛盾。所以每次增廣必然會增加一個區間。
現在就可以用線段樹維護了,相當於維護一個最大子區間值,當然,同時肯定要維護最大左區間和與最大右區間和區間和,單點修改就不用了,不然真的太毒瘤了。當然,由於有反轉操作,還需要維護最小的子區間和,反轉時一下就好,打上反轉標記。
定義一個區間型別及使用過載運算子來處理區間加可以減少程式碼量。
程式碼:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
const int maxn=1e5+7;
using namespace std;
int n,m,x,op,y,k;
struct rec{
int l,r,s;
};
rec operator + (rec x,rec y)
{
return (rec){x.l,y.r,x.s+y.s};
}
bool operator < (rec x,rec y)
{
return x.s<y.s;
}
struct node{
rec smax,smin,lmax,rmax,lmin,rmin,sum;
int rev;
}t[maxn*4];
queue <node> q;
void neww(int p,int l,int k)
{
t[p].smax=(rec){l,l,k};
t[p].lmax=(rec){l,l,k};
t[p].rmax=(rec){l,l,k};
t[p].smin=(rec){l,l,k};
t[p].lmin=(rec){l,l,k};
t[p].rmin=(rec){l,l,k};
t[p].sum=(rec){l,l,k};
}
node merge(node x,node y)
{
node z;
z.smax=max(x.smax,y.smax);
z.smax=max(z.smax,x.rmax+y.lmax);
z.smin=min(x.smin,y.smin);
z.smin=min(z.smin,x.rmin+y.lmin);
z.lmax=max(x.lmax,x.sum+y.lmax);
z.rmax=max(y.rmax,x.rmax+y.sum);
z.lmin=min(x.lmin,x.sum+y.lmin);
z.rmin=min(y.rmin,x.rmin+y.sum);
z.sum=x.sum+y.sum;
z.rev=0;
return z;
}
void clean(int p)
{
swap(t[p].smax,t[p].smin);
swap(t[p].lmax,t[p].lmin);
swap(t[p].rmax,t[p].rmin);
t[p].smax.s*=-1;
t[p].smin.s*=-1;
t[p].lmax.s*=-1;
t[p].lmin.s*=-1;
t[p].rmax.s*=-1;
t[p].rmin.s*=-1;
t[p].sum.s*=-1;
t[p].rev^=1;
}
void change(int p,int l,int r,int x,int k)
{
if (l==r)
{
neww(p,l,k);
return;
}
int mid=(l+r)/2;
if (t[p].rev)
{
clean(p*2);
clean(p*2+1);
t[p].rev^=1;
}
if (x<=mid) change(p*2,l,mid,x,k);
else change(p*2+1,mid+1,r,x,k);
t[p]=merge(t[p*2],t[p*2+1]);
}
void rev(int p,int l,int r,int x,int y)
{
if ((l==x) && (r==y))
{
clean(p);
return;
}
int mid=(l+r)/2;
if (t[p].rev)
{
clean(p*2);
clean(p*2+1);
t[p].rev^=1;
}
if (y<=mid) rev(p*2,l,mid,x,y);
else if (x>mid) rev(p*2+1,mid+1,r,x,y);
else
{
rev(p*2,l,mid,x,mid);
rev(p*2+1,mid+1,r,mid+1,y);
}
t[p]=merge(t[p*2],t[p*2+1]);
}
node query(int p,int l,int r,int x,int y)
{
if ((l==x) && (r==y)) return t[p];
int mid=(l+r)/2;
if (t[p].rev)
{
clean(p*2);
clean(p*2+1);
t[p].rev^=1;
}
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,mid),query(p*2+1,mid+1,r,mid+1,y));
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&x);
change(1,1,n,i,x);
}
scanf("%d",&m);
for (int i=1;i<=m;i++)
{
scanf("%d",&op);
if (op==0)
{
scanf("%d%d",&x,&k);
change(1,1,n,x,k);
}
else
{
scanf("%d%d%d",&x,&y,&k);
int ans=0;
while (k--)
{
node d=query(1,1,n,x,y);
if (d.smax.s<0) break;
ans+=d.smax.s;
rev(1,1,n,d.smax.l,d.smax.r);
q.push(d);
}
printf("%d\n",ans);
while (!q.empty())
{
node d=q.front();
q.pop();
rev(1,1,n,d.smax.l,d.smax.r);
}
}
}
}