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 n≤365,m≤1000。
接下来一行,有 m m m 个整数 G 0 , G 1 , ⋯ , G m − 1 G_0, G_1, \cdots, G_{m - 1} G0,G1,⋯,Gm−1。保证对于每一个 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 (1≤Ci≤300,0≤Di≤30000)。
接下来有 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 (0≤T<m,0≤L≤R≤100),其中 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;
}