Bajtman i Okrągły Robin ——费用流 线段树优化建图

题解
因为要最小割,自然想到要用最大流
建立每一个时间节点,从 S 向每个时间节点建一条流量为 1,费用为 0 的边。从对于盗贼 i,向 T 建一条流量为 1,费用为 cost(i)的边,从盗贼范围内的时间节点向盗贼建一条流量为 1,费用为 0 的边,然后跑费用流。时间效率 O(n^3),会 TLE。
然后想优化,因为时间节点的连边都是连续的,如果把时间节点离散化,效率自然就提上去了。
可以考虑类似于线段树的方式,将时间节点构造成一棵线段树,子节点向父亲节点连一条流量为 inf,费用为 0 的边。在时间向盗贼连边时就只需要将范围内的区间和盗贼相连就可以了,连的边数就优化到了 log(n)。这样子再跑费用流的效率就优化到 O(n*log^2 n)

代码

#include<bits/stdc++.h>
#define maxn 5002
#define inf 1e9
using namespace std;
struct segtree{int s,l,r;}st[maxn*4];
struct edge{int s,to,next,w,c;}e[20000002];
int ans,n,a[maxn],b[maxn],c[maxn],cnt=1,tot,S,T=1000001,mx,head[1000002],dis[1000002],flag[1000002],fa[1000002];
void add(int s,int t,int w,int c){
    e[++cnt].s=s;
    e[cnt].to=t;
    e[cnt].w=w;
    e[cnt].c=c;
    e[cnt].next=head[s];
    head[s]=cnt;}
void ins(int s,int t,int w,int c){
    add(s,t,w,c);
    add(t,s,0,-c);}
void ask(int rt,int l,int r,int x){
    if(st[rt].l>=l&&st[rt].r<=r){
        ins(st[rt].s,x,inf,0);
        return;}
    int mid=st[rt].l+st[rt].r>>1;
    if(mid>=l) ask(rt<<1,l,r,x);
    if(mid<r) ask(rt<<1|1,l,r,x);
    return;}
void build(int rt,int l,int r){
    st[rt].l=l,st[rt].r=r,st[rt].s=++tot;
    if(l==r){
        ins(S,tot,1,0);
        return;}
    int mid=(l+r)/2;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    ins(st[rt<<1].s,st[rt].s,inf,0);
    ins(st[rt<<1|1].s,st[rt].s,inf,0);
    return;}
bool spfa(){
    for(int i=0;i<=T;i++) dis[i]=inf;
    dis[0]=0;
    queue<int> q;
    q.push(0);flag[0]=1;
    while(!q.empty()){
        int now=q.front();q.pop();
        for(int k=head[now];k;k=e[k].next){
            if(e[k].w && dis[e[k].to]>dis[now]+e[k].c){
                fa[e[k].to]=k;
                dis[e[k].to]=dis[now]+e[k].c;
                if(!flag[e[k].to]){
                    flag[e[k].to]=1;
                    q.push(e[k].to);}
            }
        }flag[now]=0;}
    if(dis[T]==inf) return 0;
    return 1;}
void mcf(){
    int min1=inf;
    for(int k=fa[T];k;k=fa[e[k].s]) min1=min(min1,e[k].w);
    for(int k=fa[T];k;k=fa[e[k].s]){
        ans+=min1*e[k].c;
        e[k].w-=min1;
        e[k^1].w+=min1;}
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)
        scanf("%d%d%d",&a[i],&b[i],&c[i]),b[i]--,mx=max(b[i],mx);
    build(1,1,mx);
    for(int i=1;i<=n;i++){
        ask(1,a[i],b[i],++tot);
        ins(tot,T,1,-c[i]);}
    while(spfa()) mcf();
    cout<<-ans<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值