【网络流+可持久化线段树】[UOJ#77/BZOJ3218]A+B Problem

题目

题目链接

UOJ#77
BZOJ3218

题目描述

这里写图片描述
这里写图片描述

分析

感谢VFleaKing的博客中的讲解和geng4512的博客中的代码,涨了不少知识。

网络流的做法

首先我们来看看答案是怎么得到的

ans=iiwi+iibiiipi

我们转化一下这个等式
ans=i=1nwi+biiibiiiwiiipi

求出最小的 iibi+iiwi+iipi 即可
可以考虑最小割。
怎么建图呢?
S i连一条边,容量为 bi ,从 i T连一条边,容量为 wi
i 属于S,则选择的黑色,否则选择白色。
然后考虑奇怪的i。
构造一个 i ,使存在 {j|1<=j<i,li<=aj<=ri j 为白色} i T
,若i T i S 那么就会付出pi的代价。
我们从 i i连一条容量为 pi 的边,从 i {j|1<=j<i,li<=aj<=ri} 都连一条边,跑网络流。
求出最大流flow。
ans=i=1nwi+biflow

但是由于边十分多,显然会Memory Limit Exceeded and Time Limit Exceeded

考虑优化

用可持久化线段树优化网络流

假设 ai 互不相等:
我们以 ai 为下标
如果不考虑 1<=j<i ,i就会和一个区间内的所有点连边,似乎和线段树有关系。
我们直接把边连在区间上。
在线段树中,父亲向儿子连一条容量 + 的边,然后叶子节点在连会相应的 i 即可。
考虑1<=j<i,我们将线段树可持久化,然后第i个点向第i-1个版本的线段树连边即可。

当有存在的 {k|k<=i,ak=ai} k 时,我们这样连边之后只连接了i,没有连接 k <script type="math/tex" id="MathJax-Element-38">k</script>,怎么解决这个问题呢?
我们将每个叶子节点向它前面的一个版本的叶子节点连边即可。

代码

#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这套理论

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值