【GDKOI2014】JZOJ2020年8月13日提高組T2 石油儲備計劃
阿新 • • 發佈:2020-08-13
【GDKOI2014】JZOJ2020年8月13日提高組T2 石油儲備計劃
題目
Description
Input
Output
對於每組資料,輸出一個整數,表示達到“平衡”狀態所需的最小代價。
Sample Input
2
3
6 1 5
1 2 1
2 3 2
5
4 5 4 3 2
1 3 1
1 2 2
2 4 3
2 5 4
Sample Output
4
4
Data Constraint
對於20%的資料,N<=15
對於100%的資料,T<=10,N<=100,0<=si<=10000,1<=X,Y<=N,1<=Z<=10000。
Hint
對於第一組資料,從城市1到城市2運輸2桶石油,代價為\(1*2=2\)
對於第二組資料,從城市2到城市5運輸1桶石油,代價為\(1*4=4\);此時五個城市儲備量為(4,4,4,3,3),該狀態的非平衡度為1.2,是能達到的所有狀態的最小值。
題解
題意
給出一棵樹,有邊權
定義一個非平衡度(如題)
問如何運輸使得非平衡度最小,且代價最低
分析
很容易發現非平衡度最小有兩種情況
- 當\(sum\%n=0\),答案為\(\dfrac{sum}{n}\)
- 若不為0,答案為\(\left \lfloor\dfrac{sum}n{}\right \rfloor\)或\(\left \lfloor\dfrac{sum}n{}\right \rfloor+1\)
那麼容易想到最小費用最大流
- 所有點往源點連一條流量為油桶數量,費用為0的邊
- 所有點往匯點連一條流量為\(\left \lfloor\dfrac{sum}n{}\right \rfloor\),費用為0的邊
- 對於讀入的點,連流量正無窮,費用讀入的邊(注意是雙向,加上反向弧總共4條)
然後跑一遍最小費用最大流
隨後把所有連向匯點的邊流量加一
再跑一遍
兩次答案之和即為答案
Code
#include<bits/stdc++.h> #define rg register #define inf 999999999999 using namespace std; struct node { long long to,next,val,flow; }a[5005]; long long t,n,x,y,z,S,T,ans,ans1,tot,sum,head[5005],bj[5005],dis[5005],sy[5005]; inline void add(long long x,long long y,long long z,long long v) { a[tot].to=y; a[tot].flow=z; a[tot].val=v; a[tot].next=head[x]; head[x]=tot; ++tot; } inline long long OK() { long long plus=inf; for (rg long long i=0;i<=n+2;++i) if (bj[i]) for (rg long long j=head[i];j!=-1;j=a[j].next) if (!bj[a[j].to]&&a[j].flow) plus=min(plus,dis[a[j].to]+a[j].val-dis[i]); if (plus==inf) return 0; for (rg long long i=0;i<=n+2;++i) { if (bj[i]) { bj[i]=0; dis[i]+=plus; } } return 1; } inline long long wwl(long long now,long long ss) { if (now==T) { ans1+=dis[S]*ss; return ss; } bj[now]=1; long long u,x; for (rg long long i=head[now];i!=-1;i=a[i].next) { u=a[i].to; if (!bj[u]&&dis[u]+a[i].val==dis[now]&&a[i].flow) { x=wwl(u,min(ss,a[i].flow)); if (x) { a[i].flow-=x; a[i^1].flow+=x; return x; } } } return 0; } inline void ffl() { long long q; while (OK()) { q=wwl(S,inf); while (q) { memset(bj,0,sizeof(bj)); q=wwl(S,inf); } } } int main() { freopen("test.in","r",stdin); freopen("test.out","w",stdout); scanf("%lld",&t); while (t--) { memset(head,-1,sizeof(head)); memset(a,0,sizeof(a)); memset(dis,0,sizeof(dis)); memset(bj,0,sizeof(bj)); scanf("%lld",&n); S=n+1; T=n+2; ans=sum=ans1=0; tot=0; for (rg long long i=1;i<=n;++i) { scanf("%lld",&x); add(S,i,x,0); add(i,S,0,0); sum+=x; } for (rg long long i=1;i<n;++i) { scanf("%lld%lld%lld",&x,&y,&z); add(x,y,inf,z); add(y,x,0,-z); add(y,x,inf,z); add(x,y,0,-z); } sum/=n; for (rg long long i=1;i<=n;++i) { add(i,T,sum,0); add(T,i,0,0); } bj[S]=1; ffl(); for (rg long long i=2;i<=tot;++i) if (a[i].to==T) a[i].flow++; bj[S]=1; ffl(); printf("%lld\n",ans1); } return 0; }