E - Flow Gym - 102471E

E - Flow Gym - 102471E

题意:

n个点,m条边,从点1到点n有k条相同长度的路径,每个边都有对应的容量,你可以进行任意次操作,每次将一个边的容量-1,将另一个边的容量+1,问最少操作多少次可以使得跑最大流的结果最大

题解:

很容易发现我们可以直接计算出最大流的最大值,因为我们有所有边的容量,极端操作我们可以将所有边的容量全部分配到一条路上,也就是最大流的最大值 s u m = 所 有 流 量 综 合 一 条 路 径 的 长 度 sum=\frac{所有流量综合}{一条路径的长度} sum=
如果我们不操作,当前的最大流答案为每条路径的最小值之和,不过当前这个最大流的答案要比理想的sum小,我们如何通过操作提升到sum?
刚才分析得出,最大流的结果和每一条路径的最小值有关,那我们就每次操作提升最小值即可。我们将每一条道路上的边按照容量排序(每条路之间彼此独立,没有影响),然后每一条路径的最小容量之和,就是当前最大流sum1,sum减sum1就是我们要补的量(也就是操作次数),补完后我们开始考虑每一条路径的次小值,之和sum2就是当前最新的网络流,与sum的差距就是再次要补的量。这样一直操作,直到最新的网络流容量大于等于sum
你可能要问我们知道被补的,那谁是被扣除的呢?其实不用管,因为我们已经求了sum,也就是总有操作可以使得最大流到sum,我们每次补可以认为是容量大的一些边均摊减,我们不需要知道具体的操作方案,只需要知道操作的影响和结果即可

代码:

#include <bits/stdc++.h>
#include <unordered_map>
#define debug(a, b) printf("%s = %d\n", a, b);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
clock_t startTime, endTime;
//Fe~Jozky
const ll INF_ll= 1e18;
const int INF_int= 0x3f3f3f3f;
void read(){};
template <typename _Tp, typename... _Tps> void read(_Tp& x, _Tps&... Ar)
{
    x= 0;
    char c= getchar();
    bool flag= 0;
    while (c < '0' || c > '9')
        flag|= (c == '-'), c= getchar();
    while (c >= '0' && c <= '9')
        x= (x << 3) + (x << 1) + (c ^ 48), c= getchar();
    if (flag)
        x= -x;
    read(Ar...);
}
template <typename T> inline void write(T x)
{
    if (x < 0) {
        x= ~(x - 1);
        putchar('-');
    }
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}
void rd_test()
{
#ifdef ONLINE_JUDGE
#else
    startTime = clock ();
    freopen("data.in", "r", stdin);
#endif
}
void Time_test()
{
#ifdef ONLINE_JUDGE
#else
    endTime= clock();
    printf("\nRun Time:%lfs\n", (double)(endTime - startTime) / CLOCKS_PER_SEC);
#endif
}
const int maxn=2e5+9;
vector<pair<int,ll> >vec[maxn];
vector<ll>v[maxn];
ll ans=0;
int main()
{
    //rd_test();
	int n,m;
	read(n,m);
	ll tot=0;
	for(int i=1;i<=m;i++){
		int x,y,w;
		read(x,y,w);
		vec[x].push_back({y,w});
		tot+=w;
	}
	int cnt=0;
	for(int i=0;i<vec[1].size();i++){
		cnt++;
		int tmp=vec[1][i].first;
		v[cnt].push_back(vec[1][i].second);
		while(tmp!=n){
			v[cnt].push_back(vec[tmp][0].second);
			tmp=vec[tmp][0].first;
		}
	}
	for(int i=1;i<=cnt;i++){
		sort(v[i].begin(),v[i].end());
	}
	ll k=m/cnt;
	ll ave=tot/k;
	for(int i=0;i<k;i++){
		ll sum=0;
		for(int j=1;j<=cnt;j++)sum+=v[j][i];
		ans+=max(0ll,ave-sum);
	}
	cout<<ans<<endl;
    //Time_test();
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值