【网络流+可持久化线段树】[UOJ#77/BZOJ3218]A+B Problem
题目
题目链接
题目描述
分析
感谢VFleaKing的博客中的讲解和geng4512的博客中的代码,涨了不少知识。
网络流的做法
首先我们来看看答案是怎么得到的
我们转化一下这个等式
求出最小的
可以考虑最小割。
怎么建图呢?
从
若
然后考虑奇怪的i。
构造一个
,若
我们从
求出最大流flow。
但是由于边十分多,显然会Memory Limit Exceeded and Time Limit Exceeded
考虑优化
用可持久化线段树优化网络流
假设
我们以
如果不考虑
我们直接把边连在区间上。
在线段树中,父亲向儿子连一条容量
考虑
当有存在的
我们将每个叶子节点向它前面的一个版本的叶子节点连边即可。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define MAXN 5000
#define INF 0x7fffffff
#define MAXLOG 13
using namespace std;
queue<int>q;
void Read(int &x){
char c;
while(c=getchar(),c!=EOF)
if(c>='0'&&c<='9'){
x=c-'0';
while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';
ungetc(c,stdin);
return;
}
}
struct node{
int cap,v;
node *next,*back;
}*adj[MAXN*MAXLOG+10],edge[MAXN*100+10],*ecnt=edge;
int n,a[MAXN+10],b[MAXN+10],w[MAXN+10],l[MAXN+10],r[MAXN+10],p[MAXN+10],d[MAXN*3+10],m,S,T,ans,tot,dist[MAXN*MAXLOG+MAXN+10],flow,vd[MAXN*MAXLOG+MAXN+10];
struct Seg_tree{
Seg_tree *ch[2];
}tree[MAXN*MAXLOG+10],*tcnt=tree,*root[MAXN+10],*nil=tree;
void addedge(int u,int v,int cap){
node *p=++ecnt;
p->v=v;
p->cap=cap;
p->next=adj[u];
adj[u]=p;
p=p->back=++ecnt;
p->v=u;
p->cap=0;
p->next=adj[v];
adj[v]=p;
}
void insert(Seg_tree *&x,Seg_tree *&y,int l,int r,int i){
x=++tcnt;
*x=*y;
if(l==r){
addedge(T+x-nil,i,INF);
if(y!=nil)
addedge(T+x-nil,T+y-nil,INF); //处理相同的a[i]
return;
}
int mid=(l+r)>>1;
if(a[i]>mid)
insert(x->ch[1],y->ch[1],mid+1,r,i);
else
insert(x->ch[0],y->ch[0],l,mid,i);
if(x->ch[1])
addedge(T+x-nil,T+x->ch[1]-nil,INF);
if(x->ch[0])
addedge(T+x-nil,T+x->ch[0]-nil,INF);
}
void link(Seg_tree *x,int ll,int rr,int i){
if(ll>r[i]||l[i]>rr)
return;
if(l[i]<=ll&&r[i]>=rr){
addedge(n+i,T+x-nil,INF);
return;
}
int mid=(ll+rr)>>1;
if(x->ch[0]!=nil)
link(x->ch[0],ll,mid,i);
if(x->ch[1]!=nil)
link(x->ch[1],mid+1,rr,i);
}
void read(){
root[0]=nil;
nil->ch[0]=nil->ch[1]=nil;
Read(n);
int i;
for(i=1;i<=n;i++){
Read(a[i]),Read(b[i]),Read(w[i]),Read(l[i]),Read(r[i]),Read(p[i]);
d[++m]=a[i],d[++m]=l[i],d[++m]=r[i];
ans+=w[i]+b[i];
}
sort(d+1,d+m+1);
m=unique(d+1,d+m+1)-d-1; //离散化一下,也可以不离散化
S=(n<<1)|1,T=S+1;
for(i=1;i<=n;i++){
a[i]=lower_bound(d+1,d+m+1,a[i])-d;
l[i]=lower_bound(d+1,d+m+1,l[i])-d;
r[i]=lower_bound(d+1,d+m+1,r[i])-d;
addedge(S,i,b[i]),addedge(i,T,w[i]),addedge(i,i+n,p[i]);
}
for(i=1;i<=n;i++){
if(root[i-1]!=nil)
link(root[i-1],1,m,i);
insert(root[i],root[i-1],1,m,i);
}
tot=T+tcnt-nil;
}
void bfs(){
q.push(T);
int u;
while(!q.empty()){
u=q.front();
q.pop();
for(node *p=adj[u];p;p=p->next){
if(!dist[p->v]){
dist[p->v]=dist[u]+1;
q.push(p->v);
}
}
}
dist[T]=0;
}
int dfs(int u,int augu){
if(u==T)
return augu;
int augv=0,v,mind=tot-1,delta;
for(node *p=adj[u];p;p=p->next){
v=p->v;
if(p->cap){
if(dist[u]==dist[v]+1){
delta=min(augu-augv,p->cap);
delta=dfs(v,delta);
augv+=delta;
p->cap-=delta;
p->back->cap+=delta;
if(augv==augu||dist[S]>=tot)
return augv;
}
mind=min(mind,dist[v]);
}
}
if(!augv){
if(!--vd[dist[u]])
dist[S]=tot;
dist[u]=mind+1;
vd[dist[u]]++;
}
return augv;
}
void sap(){
bfs(); //预处理dist数组,常数优化
for(int i=1;i<=tot;i++){
if(!dist[i]){
dist[i]=tot;
continue;
}
vd[dist[i]]++;
}
vd[dist[T]=0]++;
while(dist[S]<tot)
flow+=dfs(S,INF);
}
int main()
{
read();
sap();
printf("%d\n",ans-flow);
}
现在,你已经掌握了A+B Problem这套理论