题目:http://acm.zju.edu.cn/onlinejudge/submit.do
推荐一篇博客
https://www.cnblogs.com/liu-runda/p/6262832.html
别人已经说的很好了,我能做的就是给一篇有注释的代码 (菜鸡的叹气)
#include<bits/stdc++.h>
using namespace std;
const int maxn1=200010,maxn2=300;
//struct edge{
// int to,val,nxt,i;
//}e[maxn1]; //拆成分开的数组,以 e+在结构体中的名称表示
int eto[maxn1],eval[maxn1],enxt[maxn1],ei[maxn1]; //将结构体拆成若干数组,速度有明显提升
int n,m,inds,sup_beg,sup_end,dinic_flag;
//inds:建立边的时候辅助计数 sup_beg: 超级源点(super begging) sup_end: 同理 dinic_flag: 当dinic的值等于这个才表示有附加流
int head[maxn2],flag[maxn2],deep[maxn2],ans[maxn1],low[maxn1];
// head: 会dinic的都懂 flag:判断该点是什么不守恒 deep:dinic的都懂,深度♂ low:记录每条线下限
inline int gi(){ //这个是快读,也可以用cin/ scanf代替
int f=1,sum=0;char ch=getchar();
while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return f*sum;
}
void add_edge(int fro,int to,int val,int i){
eto[inds]=to; eval[inds]=val; enxt[inds]=head[fro]; ei[inds]=i; head[fro]=inds++; //加正边
eto[inds]=fro; eval[inds]=0; enxt[inds]=head[to]; ei[inds]=i; head[to]=inds++; //反边
} // ei:记录这条边在输入时的序号,方便最后输出答案的时候知道是哪个顺序
bool bfs(){
memset(deep,0,sizeof(deep));
queue<int> Q;
Q.push(sup_beg);
deep[sup_beg]=1;
while(!Q.empty()){
int now=Q.front(); Q.pop();
for(int ind=head[now];ind!=-1;ind=enxt[ind]){
int to=eto[ind];
if(!deep[to]&&eval[ind]){
deep[to]=deep[now]+1;
Q.push(to);
}
}
}
return deep[sup_end];
}
int dfs(int num,int flow){
if(num==sup_end) return flow;
int max_flow=0;
for(int ind=head[num];ind!=-1;ind=enxt[ind]){
int to=eto[ind];
if(deep[to]==deep[num]+1&&eval[ind]){
int sto_flow=dfs(to,min(flow,eval[ind]));
flow-=sto_flow; //这句一开始我忘记加了
max_flow+=sto_flow;
eval[ind]-=sto_flow;
eval[ind^1]+=sto_flow;
if(flow==0) break;
}
}
if(max_flow==0) deep[num]=0;
return max_flow;
}
int dinic(){ //dinic嘛。不多解释了,可以看我另外一篇博客 https://blog.csdn.net/weixin_43191865/article/details/88604415
int sum=0;
while(bfs()){
sum+=dfs(sup_beg,0x3f3f3f3f);
}
return sum;
}
int main(){
int cnt=gi(); //快读快读
while(cnt--){
n=gi();
m=gi();
memset(eto,0,sizeof(eto)); memset(eval,0,sizeof(eval)); memset(ei,0,sizeof(ei));
memset(enxt,0,sizeof(enxt));
memset(head,-1,sizeof(head));
memset(flag,0,sizeof(flag));
inds=0; sup_beg=0; sup_end=n+1;
for(int i=1;i<=m;i++){
int fro=gi(),to=gi(),dow=gi(),up=gi();
add_edge(fro,to,up-dow,i);
low[i]=dow;
flag[fro]-=dow; flag[to]+=dow;
}
dinic_flag=0;
for(int i=1;i<=n;i++){
if(flag[i]>0){
add_edge(sup_beg,i,flag[i],0);
dinic_flag+=flag[i]; //因为边是双向的,只需要记录一边
}
if(flag[i]<0){
add_edge(i,sup_end,-flag[i],0);
}
}
if(dinic()==dinic_flag){
puts("YES");
memset(ans,0,sizeof(ans));
for(int i=1;i<=n;i++){
for(int ind=head[i];ind!=-1;ind=enxt[ind]){
int to=eto[ind];
if(to==sup_end||ind%2==0) continue; // 只要反边
ans[ei[ind]]=eval[ind]+low[ei[ind]];
}
}
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
}
else puts("NO");
}
return 0;
}