【BZOJ】3218: a + b Problem-最小割&线段树优化建图

传送门:bzoj3218


题解

最小割经典模型:
两个集合,每个点划分到不同集合有不同贡献,一些点集被划分到同一集合时会有额外贡献。

建图:
设与 S S S相连代表黑色,与 T T T相连代表白色。
连边 ( S , i , b i ) , ( i , T , w i ) (S,i,b_i),(i,T,w_i) (S,i,bi),(i,T,wi)。每个点 i i i拆出一个点 i ′ i′ i,连边 ( i , i ′ , p i ) , ( i ′ , j , + ∞ ) ( 1 ≤ j < i , l i ≤ a j ≤ r i ) (i,i',p_i),(i',j,+\infty)(1\leq j<i,l_i\leq a_j\leq r_i) (i,i,pi),(i,j,+)(1j<i,liajri)
答案就是 ∑ ( b i + w i ) − \sum (b_i+w_i)- (bi+wi)最小割。

主席树以 a i a_i ai为下标优化建图使得连边和点数都是 n log ⁡ n n\log n nlogn级别。
(注意主席树写法不是只有叶结点 l = r l=r l=r时才连向所有 a i = l a_i=l ai=l的点,而是每层都直接连,详见代码)


代码

copied by zsy OTZ

#include
#include
#include
#include
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 5e5+5;
const int inf = 1e9;
struct president_tree{int ls,rs;}t[N<<2];
struct edge{int to,nxt,w;}a[N];
int n,A[N],l[N],r[N],o[N],len,S,T,tot,head[N],cnt=1,dep[N],cur[N],rt,goal,ans;
queue<int>Q;
void link(int u,int v,int w){
    a[++cnt]=(edge){v,head[u],w};head[u]=cnt;
    a[++cnt]=(edge){u,head[v],0};head[v]=cnt;
}
bool bfs(){
    memset(dep,0,sizeof(dep));
    dep[S]=1;Q.push(S);
    while (!Q.empty()){
        int u=Q.front();Q.pop();
        for (int e=head[u];e;e=a[e].nxt)
            if (a[e].w&&!dep[a[e].to])
                dep[a[e].to]=dep[u]+1,Q.push(a[e].to);
    }
    return dep[T];
}
int dfs(int u,int f){
    if (u==T) return f;
    for (int &e=cur[u];e;e=a[e].nxt)
        if (a[e].w&&dep[a[e].to]==dep[u]+1){
            int tmp=dfs(a[e].to,min(a[e].w,f));
            if (tmp) {a[e].w-=tmp;a[e^1].w+=tmp;return tmp;}
        }
    return 0;
}
int dinic(){
    int res=0;
    while (bfs()){
        for (int i=1;i<=tot;++i) cur[i]=head[i];
        while (int tmp=dfs(S,inf)) res+=tmp;
    }
    return res;
}
void query(int x,int l,int r,int ql,int qr){
    if (l>=ql&&r<=qr) {link(goal,x,inf);return;}
    int mid=l+r>>1;
    if (ql<=mid) query(t[x].ls,l,mid,ql,qr);
    if (qr>mid) query(t[x].rs,mid+1,r,ql,qr);
}
void modify(int &x,int l,int r,int p){
    t[++tot]=t[x];link(tot,x,inf);x=tot;
    link(x,goal,inf);
    if (l==r) return;int mid=l+r>>1;
    if (p<=mid) modify(t[x].ls,l,mid,p);else modify(t[x].rs,mid+1,r,p);
}
int main(){
    n=gi();S=2*n+1;T=tot=2*n+2;
    for (int i=1,b,w,p;i<=n;++i){
        A[i]=gi(),b=gi(),w=gi(),ans+=b+w;
        l[i]=gi(),r[i]=gi(),p=gi();
        link(S,i,b),link(i,T,w);
        link(i,i+n,p);
        o[++len]=A[i];o[++len]=l[i];o[++len]=r[i];
    }
    sort(o+1,o+len+1);len=unique(o+1,o+len+1)-o-1;
    for (int i=1;i<=n;++i){
        A[i]=lower_bound(o+1,o+len+1,A[i])-o;
        l[i]=lower_bound(o+1,o+len+1,l[i])-o;
        r[i]=lower_bound(o+1,o+len+1,r[i])-o;
        goal=n+i,query(rt,1,len,l[i],r[i]);
        goal=i,modify(rt,1,len,A[i]);
    }
    printf("%d\n",ans-dinic());
    return 0;
}

你可能感兴趣的:(最大流最小割,主席树)