bzoj3272 Zgg吃東西(線段樹模擬費用流)
阿新 • • 發佈:2019-02-14
題目大意
給定數列,要求維護以下操作和詢問:
- 將賦值為
- 在區間中選出最多個互不相交的子段列,最大化這些選中的數的和,輸出這個最大值
操作和詢問共個
分析:
首先看一下暴力怎麼解決這個問題:
把一個數拆成兩個點,作為部和部
部的點向部的對應點連邊,容量為1,費用為
原點向部連邊,容量為1,費用為0
部向匯點連邊,容量為1,費用為0
相鄰點從部連向部,容量為1,費用為0
跑次流量為1的最大費用流即可
上述方法邊數有,顯然會TLE
那麼我們就需要看出演算法的實質:
每次增廣的過程,實質上就是取一段和最大的子序列
很顯然對於這類序列上的操作,可以用線段樹去實現
正解
費用流的構圖,線段樹手動模擬增廣過程
線段樹維護方法:
維護一段區間的最大子序列
每次我們提取出一個最大子序列時,我們要把這個子序列取反(*-1,防止重複選擇),所以還需要維護最小子序列
每進行一次取反,當前最大和子序列一定變成最小和子序列,最小和子序列一定變成最大,那麼直接swap一下就可以了
鑑於一次詢問需要增廣K次,每一次都要要取反,所以需要開一個棧記錄一下當前詢問所反轉的所有區間,在結束時還原
總的時間複雜度是
看一下維護:
struct node{
int lx,rx,mx,sum;
int lp,rp,p1,p2;
void init(int l,int val) {
lp=rp=p1=p2=l;
lx=rx=mx=val;
sum=val;
}
};
struct Tree{
int l,r,a,b;
bool flag;
node mn,mx;
void init(int val) {
mn.init(l,-val);
mx.init(l,val);
}
};
Tree t[N<<2];
:從左端點開始的最大子序列
:從右端點開始的最大子序列
:整個區間的最大子序列
:區間和
:的右端點
:的左端點
:的左端點
:的右端點
:區間翻轉標記
:記錄區間的最大子序列
:記錄區間的最小子序列
函式:插入一個值(mx正值,mn負值)
void push(int bh) {
if (t[bh].l==t[bh].r) return;
if (t[bh].flag) {
swap(t[bh<<1].mx,t[bh<<1].mn);
swap(t[bh<<1|1].mx,t[bh<<1|1].mn);
t[bh<<1].flag^=1; t[bh<<1|1].flag^=1;
t[bh].flag^=1;
}
}
處理區間翻轉:
node merge(node a,node b) {
node t;
t.sum=a.sum+b.sum;
t.lx=a.lx; t.lp=a.lp;
if (a.sum+b.lx>t.lx) t.lx=a.sum+b.lx,t.lp=b.lp;
t.rx=b.rx; t.rp=b.rp;
if (b.sum+a.rx>t.rx) t.rx=b.sum+a.rx,t.rp=a.rp;
t.mx=a.rx+b.lx;
t.p1=a.rp; t.p2=b.lp;
if (t.mx<a.mx) t.mx=a.mx,t.p1=a.p1,t.p2=a.p2;
if (t.mx<b.mx) t.mx=b.mx,t.p1=b.p1,t.p2=b.p2;
return t;
}
重要的合併函式
按照各變數的意義轉移即可
void solve(int l,int r,int k) {
int ans=0;
top=0;
while (k--) {
node t=ask(1,l,r);
if (t.mx>0) ans+=t.mx;
else break;
rever(1,t.p1,t.p2);
++top;
q[top].x=t.p1; q[top].y=t.p2;
}
for (int i=top;i>0;i--)
rever(1,q[i].x,q[i].y); //消除影響
printf("%d\n",ans);
}
每次我們提取出一個最大子序列,加入答案(如果小於0就停止操作)
q是記錄翻轉區間的棧,處理完之後消除翻轉影響
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
const int N=100005;
struct node{
int lx,rx,mx,sum;
int lp,rp,p1,p2;
void init(int l,int val) {
lp=rp=p1=p2=l;
lx=rx=mx=val;
sum=val;
}
};
struct Tree{
int l,r,a,b;
bool flag;
node mn,mx;
void init(int val) {
mn.init(l,-val);
mx.init(l,val);
}
};
Tree t[N<<2];
struct point{
int x,y;
};
point q[N];
int n,m,a[N],top=0;
void push(int bh) {
if (t[bh].l==t[bh].r) return;
if (t[bh].flag) {
swap(t[bh<<1].mx,t[bh<<1].mn);
swap(t[bh<<1|1].mx,t[bh<<1|1].mn);
t[bh<<1].flag^=1; t[bh<<1|1].flag^=1;
t[bh].flag^=1;
}
}
node merge(node a,node b) {
node t;
t.sum=a.sum+b.sum;
t.lx=a.lx; t.lp=a.lp;
if (a.sum+b.lx>t.lx) t.lx=a.sum+b.lx,t.lp=b.lp;
t.rx=b.rx; t.rp=b.rp;
if (b.sum+a.rx>t.rx) t.rx=b.sum+a.rx,t.rp=a.rp;
t.mx=a.rx+b.lx;
t.p1=a.rp; t.p2=b.lp;
if (t.mx<a.mx) t.mx=a.mx,t.p1=a.p1,t.p2=a.p2;
if (t.mx<b.mx) t.mx=b.mx,t.p1=b.p1,t.p2=b.p2;
return t;
}
void update(int bh) {
t[bh].mn=merge(t[bh<<1].mn,t[bh<<1|1].mn);
t[bh].mx=merge(t[bh<<1].mx,t[bh<<1|1].mx);
}
void build(int bh,int l,int r) {
t[bh].l=l; t[bh].r=r;
if (l==r) {
t[bh].init(a[l]);
return;
}
int mid=(l+r)>>1;
build(bh<<1,l,mid);
build(bh<<1|1,mid+1,r);
update(bh);
}
void rever(int bh,int L,int R) {
push(bh);
int l=t[bh].l,r=t[bh].r,mid=(l+r)>>1;
if (l>=L&&r<=R) {
swap(t[bh].mn,t[bh].mx);
t[bh].flag^=1;
return;
}
if (L<=mid) rever(bh<<1,L,R);
if (R>mid) rever(bh<<1|1,L,R);
update(bh);
}
node ask(int bh,int L,int R) {
push(bh);
int l=t[bh].l,r=t[bh].r,mid=(l+r)>>1;
if (l==L&&r==R) return t[bh].mx;
if (R<=mid) return ask(bh<<1,L,R);
else if (L>mid) return ask(bh<<1|1,L,R);
else return merge(ask(bh<<1,L,mid),ask(bh<<1|1,mid+1,R));
}
void solve(int l,int r,int k) {
int ans=0;
top=0;
while (k--) {
node t=ask(1,l,r);
if (t.mx>0) ans+=t.mx;
else break;
rever(1,t.p1,t.p2);
++top;
q[top].x=t.p1; q[top].y=t.p2;
}
for (int i=top;i>0;i--)
rever(1,q[i].x,q[i].y); //消除影響
printf("%d\n",ans);
}
void change(int bh,int pos,int val) {
push(bh);
int l=t[bh].l,r=t[bh].r,mid=(l+r)>>1;
if (l==r) {
t[bh].init(val); return;
}
if (pos<=mid) change(bh<<1,pos,val);
else change(bh<<1|1,pos,val);
update(bh);
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
build(1,1,n);
scanf("%d",&m);
int opt,l,r;
while (m--) {
scanf("%d%d%d",&opt,&l,&r);
if (opt==1) {
int x; scanf("%d",&x);
solve(l,r,x);
}
else change(1,l,r);
}
return 0;
}