BZOJ4669 抢夺 / 洛谷P4400 Blue Mary的旅行

洛谷链接

这是一道很有意思的题目。

经过一番化简,我们可以把题目理解为求得最少使所有人都到达的最少时间为 \(day\) ,同时对于这个时间我们找到了若干条不冲突的方案 \(dis_i\)\(p_i\) 表示路径的长度和一批有多少人到达,使得其满足:

\[k \le \sum (k - dis_i + 1) \times p_i \]

化简可得:

\[k + \sum dis_i \times p_i \le k \times \sum p_i \]

因此我们只要求得了 \(\sum p_i\)\(\sum dis_i \times p_i\) 就可以快速求得答案。

我们发现他的单位时间内的限制与网络流的流量限制很像,同时 \(\sum dis_i \times p_i\) 又和费用流的费用计算很像,因此我们考虑使用费用流辅助。但是由于总流量不确定,我们考虑枚举总流量。容易发现,在前期费用增长比较缓慢,而后期变化量较大,因此这是一个单峰函数,可以三分。

注意,在三分时,我们要把天数以浮点数形式比较,以防止在向上去整后平台的出现对正确性的影响。(虽然没有任何一个数据卡了这一点

得到如下代码:

#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;

template <typename T>
void read(T &x) {
	T f=1;x=0;char s=getchar();
	while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
	while(s>='0'&&s<='9') {x=(x<<3)+(x<<1)+(s^'0');s=getchar();}
	x *= f;
}

const int MAXN = 1e5 + 5;
const int MAXM = 1e5 + 5;
const LL inf = 1e15;

int head[MAXN] , to[MAXM << 1] , nxt[MAXM << 1] , cnt = 1;
LL edge[MAXM << 1] , val[MAXM << 1];
void add(int u , int v , LL c , LL w) {
	nxt[++cnt] = head[u];head[u] = cnt;to[cnt] = v;edge[cnt] = c;val[cnt] = w;
	nxt[++cnt] = head[v];head[v] = cnt;to[cnt] = u;edge[cnt] = 0;val[cnt] = -w;
}

int las[MAXN] , pre[MAXN] , num , s , t , vis[MAXN];
LL dis[MAXN] , flow[MAXN];

struct MinCostMaxFlow {
	LL MaxFlow , MinCost;
	bool bfs() {
		for (int i = 1; i <= num; ++i) las[i] = 0 , dis[i] = inf , pre[i] = 0 , flow[i] = 0 , vis[i] = 0;
		flow[s] = inf , dis[s] = 0;
		queue <int> q;q.push(s);
		int flag = 0;
		while(!q.empty()) {
			int x = q.front();
			q.pop();
			vis[x] = 0;
			for (int i = head[x]; i; i = nxt[i]) {
				if(!edge[i]) continue;
				int v = to[i];
				if(dis[v] > dis[x] + val[i]) {
					dis[v] = dis[x] + val[i];
					flow[v] = min(flow[x] , edge[i]);
					pre[v] = x;
					las[v] = i;
					if(v == t) {
						flag = 1;
						continue;
					}
					if(!vis[v]) vis[v] = 1 , q.push(v);
				}
			}
		} 
		return flag;
	}
	void MVMC() {
		MaxFlow = 0 , MinCost = 0;
		while(bfs()) {
			int now = t;
			MaxFlow += flow[t];
			MinCost += dis[t] * flow[t];
			while(now != s) {
				edge[las[now]] -= flow[t];
				edge[las[now] ^ 1] += flow[t];
				now = pre[now];
			}
		}
	} 
}MIN;

int n , m , k;

double cal(int x) {
	for (int i = 2; i <= cnt; i += 2) edge[i] += edge[i ^ 1] , edge[i ^ 1] = 0;
	edge[cnt - 1] = x;
	MIN.MVMC();
	return (k + MIN.MinCost) * 1.0 / MIN.MaxFlow;
}

int main() {
//	freopen("snatch.in" , "r" , stdin);
//	freopen("snatch.out" , "w" , stdout)
	while(scanf("%d %d %d" , &n , &m , &k) != EOF) {
		num = n + 2;
		s = n + 2 , t = n;
		for (int i = 1; i <= m; ++i) {
			int u , v , c;
			read(u),read(v),read(c);
//			u ++ , v ++;
			add(u , v , c , 1);
		}
		if(!k) {
			puts("0");
			cnt = 1;
			for (int i = 1; i <= num; ++i) head[i] = 0; 
			continue;
		}
		add(s , 1 , inf , 0);
		MIN.MVMC();
		if(MIN.MaxFlow == 0) {
			puts("No solution");
			cnt = 1;
			for (int i = 1; i <= num; ++i) head[i] = 0; 
			continue;
		}
		int l = 1 , r = MIN.MaxFlow;
		LL ans = (k + MIN.MinCost + MIN.MaxFlow - 1) / MIN.MaxFlow - 1;
		while(l <= r) {
			int mid = (l + r) >> 1;
			double ans1 = cal(mid) , ans2 = cal(mid + 1);
			if(ans1 > ans2) l = mid + 1 , ans = min(ans , (LL)ceil(ans2));
			else r = mid - 1 , ans = min(ans , (LL)ceil(ans1));
		}
		printf("%lld\n" , ans);
		cnt = 1;
		for (int i = 1; i <= num; ++i) head[i] = 0; 
	}
	
	return 0;
}
posted @ 2021-01-05 22:02  Reanap  阅读(57)  评论(0编辑  收藏  举报