Zoj3229 Shoot the Bullet|东方文花帖|【模板】有源汇上下界最大流

文章讲述了射命丸文在幻想乡拍摄少女照片的故事,需遵循特定的拍摄张数限制和时间条件,涉及有源汇上下界的最大流算法来最大化照片数量。
摘要由CSDN通过智能技术生成

Zoj3229 Shoot the Bullet|东方文花帖|【模板】有源汇上下界最大流

题目背景

Translated by @chen_zhe

幻想乡是一个被博丽大结界和虚幻与现实的境界所包围起来的一个美妙的地方。这里人和其他生物,例如妖怪、妖精等核平共处。

射命丸文(Syameimaru Aya)是一只鸦天狗,拥有操纵风的能力,已经活了千岁以上,是《文文。新闻》的主编,拥有着一本叫做《文花帖》的手账,记录幻想乡各地的大新闻。她不仅是天狗中速度最快的鸦天狗,思考能力非常强,以别人的几倍的思考速度思考,也拥有幻想乡最高等级的力量。

(译者内心 O.S.:远古的东方众都那么硬核科普的吗)

题目描述

(附注:文花帖8-8 西行寺幽幽子 「死蝶浮月」)

在接下来的 n n n 天中,射命丸文将要拍摄幻想乡的少女的照片。并且要为第 x x x 个少女拍摄至少 G x G_x Gx 张照片刊登在《文文。新闻》上。在第 k k k 天的时候文文有 C k C_k Ck 位少女可以拍,且在这一天中对某个少女拍的照片数量 必须 在某个闭区间 [ L , R ] [L, R] [L,R] 中。如果过少,文文就搞不出大新文;如果过多,就会有少女很安格瑞。

除此之外,因为拍照设备的原因,对于第 i i i 天,每天的拍照数量不能超过 D i D_i Di 张。在满足这些限定的条件下,文文希望拍到的照片尽可能地多。

由于文文需要去取材,因此她把这个任务交给了你,让你帮她去解决。

输入格式

本题有不定组数据,保证数据组数不超过 10 10 10

第一行输入两个非负整数 n n n m m m,分别表示有 n n n 天,有 m m m 位少女。其中 n ≤ 365 , m ≤ 1000 n \leq 365,m \leq 1000 n365,m1000

接下来一行,有 m m m 个整数 G 0 , G 1 , ⋯   , G m − 1 G_0, G_1, \cdots, G_{m - 1} G0,G1,,Gm1。保证对于每一个 G x G_x Gx,都满足 G x ∈ [ 0 , 1 0 5 ] G_x \in [0,10^5] Gx[0,105]

再接下来有 n n n 段,第 i i i 段的第一行有两个整数 C i , D i   ( 1 ≤ C i ≤ 300 , 0 ≤ D i ≤ 30000 ) C _ i, D _ i\ (1 \leq C _ i \leq 300, 0 \leq D _ i \leq 30000) Ci,Di (1Ci300,0Di30000)

接下来有 C i C _ i Ci 行,每一行有三个非负整数 T , L , R   ( 0 ≤ T < m , 0 ≤ L ≤ R ≤ 100 ) T,L,R\ (0 \leq T < m, 0 \leq L \leq R \leq 100) T,L,R (0T<m,0LR100),其中 T T T 指的是少女的编号。

输出格式

如果无法满足文文的需求,那么请输出 -1

否则请输出在满足需求的情况下,文文最多能拍多少张照片。

注意每输出完一组数据之后,中间要空一行。

样例 #1

样例输入 #1

2 3
12 12 12
3 18
0 3 9
1 3 9
2 3 9
3 18
0 3 9
1 3 9
2 3 9

样例输出 #1

36

样例 #2

样例输入 #2

2 3
12 12 12
3 18
0 3 9
1 3 9
2 3 9
3 18
0 3 9
1 3 9
2 3 9
2 3
12 12 12
3 18
0 3 9
1 3 9
2 3 9
3 18
0 3 9
1 3 9
2 3 9

样例输出 #2

36

36

思路

根据题目的意思,我们可以:
在这里插入图片描述

代码

//做法:有源汇上下界我们只需要把源点和汇点连起来,然后采取跟无源汇上下界的同一个思路,建立虚拟原点。
//然后我们惊奇的发现,在对于满流的S->T图中的任意个满流,如果它加上s->t的可行流,我们可以知道它的和就为S->T的
//满流,为什么呢?因为满足流量守恒和容量限制,而且它两相减直接就等于s->t的可行流。
//所以得出公式:f=f0+f(s->t)(具体公式的含义见上述图片)
//所以我们只需要先跑一遍无源汇的最大流,然后再删去从t->s的那条边再跑一遍最大流即可求出解

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#define int long long

using namespace std;

const int N = 500010,M = (N+10000)*2+10,INT = 1e9+7;

int e[M],ne[M],f[M],h[N],idx;
int cur[N],d[N],A[N];//A[i]表示入边-出边
int n,m,S,T,C,D;

void add(int a,int b,int c){
    e[idx]=b,f[idx]=c,ne[idx]=h[a],h[a]=idx++;
    e[idx]=a,f[idx]=0,ne[idx]=h[b],h[b]=idx++;
}

bool bfs(){
    queue<int>q;
    
    memset(d,-1,sizeof d);
    
    d[S]=0;
    
    q.push(S);
    
    cur[S]=h[S];
    
    while(q.size()){
        int t=q.front();
        q.pop();
        
        for(int i=h[t];~i;i=ne[i]){
            int ver=e[i];
            
            if(d[ver]==-1&&f[i]){
                d[ver]=d[t]+1;
                cur[ver]=h[ver];
                if(ver==T)return true;
                q.push(ver);
            }
        }
    }
    return false;
}

int find(int u,int lim){
    if(u==T)return lim;
    
    int flow=0;
    
    for(int i=cur[u];~i&&flow<lim;i=ne[i]){
        cur[u]=i;
        
        int ver=e[i];
        
        if(d[ver]==d[u]+1&&f[i]){
            int t=find(ver,min(f[i],lim-flow));
            if(!t)d[ver]=-1;
            f[i]-=t,f[i^1]+=t,flow+=t;
        }
    }
    return flow;
    
}

int dinic(){
    int r=0,flow;
    
    while(bfs())while((flow=find(S,INT)))r+=flow;
    
    return r;
}

signed main(){
    while(cin>>n>>m){
        memset(A,0,sizeof A);
        memset(h,-1,sizeof h);
        idx=0;
        int s,t;
        s=0,t=n+m+1;
        
        for(int i=1;i<=m;i++){
            int x;
            cin>>x;
            add(s,i,INT-x);//因为至少要拍x张
            A[s]-=x,A[i]+=x;
        }
        
        for(int i=1;i<=n;i++){
            cin>>C>>D;
            add(m+i,t,D);//因为每天拍摄不超过D张
            for(int j=1;j<=C;j++){
                // int a,b,c;
                // cin>>a>>b>>c;
                // add(a+1,m+i,c-b);
                // A[a+1]-=c,A[m+i]+=c;
                int x,L,R;
                cin>>x>>L>>R;
                add(x+1,m+i,R-L);
                A[x+1]-=L,A[m+i]+=L;
            }
        }
        
        S=n+m+2,T=n+m+3;
        int tot=0;
        for(int i=0;i<=n+m+1;i++){
            if(A[i]>0)add(S,i,A[i]),tot+=A[i];
            else if(A[i]<0)add(i,T,-A[i]);
        }
        
        add(t,s,INT);
        
        if(dinic()!=tot){
            puts("-1");
            puts("");
        }else{
            int res=f[idx-1];
            
            f[idx-1]=f[idx-2]=0;
            
            S=s,T=t;
            
            cout<<(res+dinic())<<endl;
            puts("");
        }
        
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

green qwq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值