BZOJ3218: A + B Problem

本文详细解析了BZOJ3218 A+B Problem的解题思路,利用主席树优化最小割算法,并分享了作者调试过程中的心得与教训。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

BZOJ3218: A + B Problem

最小割·主席树

题解:

用主席树优化最小割orz

orz orz orz orz orz
orz orz orz orz orz
orz POPOQQQ orz
orz orz orz orz orz
orz orz orz orz orz

调试1.5h+

Code:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#define D(x) cout<<#x<<" = "<<x<<"  "
#define E cout<<endl 
using namespace std;
typedef long long ll;
const int N = 200005;
const int INF = 0x3f3f3f3f;

inline void read(int &num){
    num=0; char c; int bo=1;
    while(!isdigit(c=getchar()) && c!='-');
    if(c=='-') bo=-1;
    else num=c-'0';
    while(isdigit(c=getchar())) num=num*10+c-'0';
    num*=bo;
}

int S, T, n, tp[N], tpsz; ll ans;
int a[5005],b[5005],w[5005],l[5005],r[5005],p[5005];
int lch[N], rch[N], root[N], sz;

struct Edge{
    int to,next,cap,flow;
} e[2000005];
int head[N], ec=1;
void add(int a,int b,int cap){
    ec++; e[ec].cap=cap; e[ec].flow=0;
    e[ec].to=b; e[ec].next=head[a]; head[a]=ec;
}
void add2(int a,int b,int cap){
    add(a,b,cap); add(b,a,0);
}

int d[N]; bool vis[N];
bool bfs(){
    memset(vis,false,sizeof(vis));
    memset(d,-1,sizeof(d));
    queue<int> q; q.push(S); vis[S]=true; d[S]=0;
    while(!q.empty()){
        int u=q.front(); q.pop();
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].to;
            if(!vis[v] && e[i].cap>e[i].flow){
                vis[v]=true; d[v]=d[u]+1; q.push(v);
                if(v==T) return true;
            }
        }
    }
    return false;
}

int dfs(int u,int a){
    if(u==T || a==0) return a;
    int flow=0, f;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(d[v]==d[u]+1 && (f=dfs(v,min(a,e[i].cap-e[i].flow)))){
            e[i].flow+=f; e[i^1].flow-=f; flow+=f; a-=f;
            if(a==0) break;
        }
    }
    if(a) d[u]=-1;
    return flow;
}

ll dinic(){
    ll flow=0;
    while(bfs()){ flow+=dfs(S,INF); }
    return flow;
}

void insert(int &x,int t,int p,int id,int l=1,int r=tpsz){
    x=++sz; lch[x]=lch[t]; rch[x]=rch[t];
    add2(id,x+n*2,INF); if(t)add2(t+n*2,x+n*2,INF);     
    if(l!=r){
        int mid=(l+r)>>1;
        if(p<=mid){ insert(lch[x],lch[t],p,id,l,mid); }
        else{ insert(rch[x],rch[t],p,id,mid+1,r); }
    }
}

void find(int &x,int t,int ql,int qr,int id,int l=1,int r=tpsz){
    if(!x) return;
    if(ql<=l && r<=qr){ add2(x+n*2,id+n,INF); return; }
    int mid=(l+r)>>1;
    if(ql<=mid){ find(lch[x],lch[t],ql,qr,id,l,mid); }
    if(qr>mid){ find(rch[x],rch[t],ql,qr,id,mid+1,r); }
}

void build(){
    S=N-5; T=S+1;
    for(int i=1;i<=n;i++){
        add2(S,i,w[i]); add2(i,T,b[i]);
        add2(i+n,i,p[i]);
    }

    for(int i=1;i<=n;i++){
        if(i>1) find(root[i-1],root[i-2],l[i],r[i],i);
        insert(root[i],root[i-1],a[i],i);
    }
}

int main(){
    freopen("3218.in","r",stdin);
    read(n); tpsz=0;
    for(int i=1;i<=n;i++){
        read(a[i]); read(b[i]); read(w[i]); 
        read(l[i]); read(r[i]); read(p[i]);
        ans+=w[i]; ans+=b[i];
        tp[++tpsz]=a[i]; tp[++tpsz]=l[i]; tp[++tpsz]=r[i];
    }
    sort(tp,tp+tpsz); tpsz=unique(tp,tp+tpsz)-tp;
//  D(tpsz); E;
    for(int i=1;i<=n;i++){
        a[i]=lower_bound(tp,tp+tpsz,a[i])-tp+1;
        l[i]=lower_bound(tp,tp+tpsz,l[i])-tp+1;
        r[i]=lower_bound(tp,tp+tpsz,r[i])-tp+1;
//      D(a[i]); D(l[i]); D(r[i]); E;
    }
    build();
    ans-=dinic();
    printf("%lld\n",ans);
}

最后来写一下我为啥一直WA。。。
出来手残少些了一些addedge外
罪魁祸首就是想着在一个root里面插入多条链,还想着这几条链公共部分共用点的zz主席树(你明白这是啥吧。。。不知道也没关系,可能只有我这种zz能想出这种zz写法。。。)
我一开始在查找区间的时候走到没有建立的点也新建点,其实根本就不用,直接返回就行了。因为下面没有点,反正也不会有流量。

总之不要像我这样zz!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值