【UOJ#77】A+B Problem
可持久化线段树优化建图,最小割
题目大意:
有 $n$ 个点,第 $i$ 个点染成黑色得到 $b_i$ 的价值,染成白色得到 $w_i$ 的价值。
如果第 $i$ 个点是黑色,且存在 $1\leq j\lt i$ ,满足 $l_i\leq a_j\leq r_i$ 且 $j$ 是白色,则减少 $p_i$ 的价值。
求最大总价值。
题解:
考虑将问题变成一个最小割问题。
我们将源点连向 $i$ 的边看作染黑,$i$ 连向汇点的边看作染白。则割一条边相当于不染这种颜色。一个点只能有一种颜色,所以要求源汇不连通。要求减去的代价最小,则求最小割即可。
考虑限制关系。我们枚举 $j$,对满足 $j\lt i$ 且 $l_i\leq a_j\leq r_i$ 的点 $j$,由 $i$ 向 $j$ 连容量为 $p_i$ 的边。这样若 $i$ 选黑而 $j$ 选白,则源点通过 $i$ 和 $j$ 到汇点连通,因此要割掉这条边,花费代价为 $p_i$。
对建出来的图跑最大流,用 $b$ 和 $w$ 的总和减去最大流即可。
边数是 $O(n^2)$ 的,无法接受。
考虑优化建图。
我们用可持久化线段树对每个 $i$ 维护 $1\sim i$ 结点的连边的树形结构,然后对每个 $i$,在可持久化线段树上找到 $l_i,r_i$ 对应的区间连边即可。要对 $a$ 进行离散化,否则点数和边数将会乘 $\log a$ 而不是 $\log n$,导致效率低下。
这样点数和边数都是 $O(n\log n)$ 的,可以通过。
注意可持久化线段树优化建图的一些细节,新的点要向原来的点连边。
Code:
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int N=5005,M=220005;
typedef long long LL;
const LL inf=1e16;
int n,head[M],iter[M],cnt=1,T,nod,rt[N],d[M],ls[M],rs[M],n_sg,dep[M];
LL sum,fl;
struct edge{
int to,nxt;LL cap;
}e[M*4];
inline void addedge(int u,int v,LL cap){
e[++cnt]=(edge){v,head[u],cap},head[u]=cnt;
e[++cnt]=(edge){u,head[v],0},head[v]=cnt;
}
void addnode(int&o,int pre,int l,int r,const int&pos,const int&id){
o=++n_sg,ls[o]=ls[pre],rs[o]=rs[pre],d[o]=++nod;
if(pre)addedge(d[o],d[pre],inf);
if(l<r){
const int mid=(l+r)/2;
if(pos<=mid)addnode(ls[o],ls[pre],l,mid,pos,id);
else addnode(rs[o],rs[pre],mid+1,r,pos,id);
if(ls[o])addedge(d[o],d[ls[o]],inf);
if(rs[o])addedge(d[o],d[rs[o]],inf);
}else addedge(d[o],id,inf);
}
void link(int o,int l,int r,const int&L,const int&R,const int&id){
if(!o)return;
if(L<=l&&r<=R)addedge(id,d[o],inf);else{
const int mid=(l+r)/2;
if(L<=mid)link(ls[o],l,mid,L,R,id);
if(mid<R)link(rs[o],mid+1,r,L,R,id);
}
}
queue<int>q;
void bfs(){
for(q.push(0);!q.empty();){
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nxt)
if(e[i].cap&&!dep[e[i].to]){
dep[e[i].to]=dep[u]+1;
q.push(e[i].to);
}
}
}
LL dfs(int u,LL f){
if(u==T||!f)return f;
for(int&i=iter[u];i;i=e[i].nxt)if(e[i].cap&&dep[e[i].to]>dep[u]){
LL d=dfs(e[i].to,min(f,e[i].cap));
if(d){
e[i].cap-=d,e[i^1].cap+=d;
return d;
}
}
return 0;
}
void flow(){
for(;;){
memset(dep,0,sizeof dep);
dep[0]=1;
bfs();
if(!dep[T])break;
memcpy(iter,head,sizeof iter);
while(LL f=dfs(0,inf))fl+=f;
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
T=nod=n+1;
static int A[N],B[N],W[N],L[N],R[N],P[N],db[N];
for(int i=1;i<=n;++i)cin>>A[i]>>B[i]>>W[i]>>L[i]>>R[i]>>P[i],db[i]=A[i];
sort(db+1,db+n+1);
int M=unique(db+1,db+n+1)-db-1;
for(int i=1;i<=n;++i){
int a=A[i],b=B[i],w=W[i],l=L[i],r=R[i],p=P[i];
a=lower_bound(db+1,db+M+1,a)-db;
l=lower_bound(db+1,db+M+1,l)-db;
r=upper_bound(db+1,db+M+1,r)-db-1;
addnode(rt[i],rt[i-1],1,M,a,i);
sum+=b+w;
addedge(0,i,b),addedge(i,T,w);
int x=++nod;
addedge(i,x,p);
link(rt[i-1],1,M,l,r,x);
}
flow();
cout<<sum-fl<<'\n';
return 0;
}