Educational Codeforces Round 102 (Rated for Div. 2)

ABCD略

E.

题意:给定一张n个节点m条边的无向图,定义路径长度(看上图),求最短路.

首先,我们看式子就是总边权-最大边权+最小边权,就相当于略过一条最大边,然后计算两倍的最小边,我们弱化这个问题,求1->i的略过任意一条边,并且将另一条边的边权计算两倍的最小值,显然,略过最大边,选择最小边的两倍是最优解,于是我们对这个弱化的问题进行dp就行.那就是一个典型的图上背包了,可以分层图做,题主这里用dp,f[i][00/01/10/11]表示到i号点两个选择的选择策略(左边的1表示略过,右边的1表示计算两倍边权)

#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define int long long
#define PII pair<int, int>
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N = 2e5 + 10;
int n, m, f[N][5], vis[N][5];
struct rec {
	int len, x, st;
	bool operator< (const rec &i) const {
		return len > i.len;
	}
};
vector<PII> g[N];
priority_queue<rec> heap;
signed main() {
	IOS;
	cin >> n >> m;
	for (int i = 1; i <= m; ++i) {
		int x, y, z;
		cin >> x >> y >> z;
		g[x].emplace_back(y, z);
		g[y].emplace_back(x, z);
	}
	for (int i = 1; i <= n; ++i) {
		for (int j = 0; j <= 3; ++j) {
			f[i][j] = INF;
		}
	}
	f[1][0] = 0;
	heap.push({0, 1, 0});
	while (heap.size()) {
		auto [_, x, st] = heap.top(); heap.pop();
		// cout << x << " " << st << " " << _ << "\n";
		if (vis[x][st]) continue;
		vis[x][st] = 1;
		for (auto [y, z] : g[x]) {
			for (int i = 0; i <= 3; ++i) {
				if ((i | st) == i && i - st <= 2) {
					// cout << y << " " << z << " " << i << "==\n";
					int dl = i - (i & st);
					if (dl == 0) f[y][i] = min(f[y][i], f[x][st] + z);
					else if (dl == 1) f[y][i] = min(f[y][i], f[x][st] + z * 2);
					else if (dl == 2) f[y][i] = min(f[y][i], f[x][st]);
					heap.push({f[y][i], y, i});
				}
			}
		}
	}
	for (int i = 2; i <= n; ++i) {
		cout << min(f[i][3], f[i][0]) << " ";
	}
}

F.

题意:n个数a[1~n],每个数权值b[i],如果选择a[i],那么必须选择(1~i-1)所有能够整除a[i]的数,求选出的数的最小权值.

是一道很典的最小割板子题,建图对于每个a[i]的约数求出假设其最后出现的位置为j,那么i -> j,流量INF,然后如果b[i]<0,S->i,b[i]>0, i->T,跑一次最大流得到maxflow,答案为所有正值b[i]之和 - maxflow.

#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define int long long
#define PII pair<int, int>
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N = 3010, M = N * 100;
int n, a[N], b[N], pos[N];
int h[M], w[M], v[M], to[M], tot = 1;
int maxflow, flow, d[N], now[M], S, T;
bool bfs() {
	for (int i = S; i <= T; ++i) d[i] = 0;
	queue<int> q;
	q.emplace(S); d[S] = 1; now[S] = h[S];
	while (q.size()) {
		int x = q.front(); q.pop();
		for (int i = h[x], y; i; i = to[i]) {
			if (w[i] && !d[y = v[i]]) {
				q.emplace(y);
				now[y] = h[y];
				d[y] = d[x] + 1;
				if (y == T) return true;
			}
		}
	}
	return false;
}
int dinic(int x, int flow) {
	if (x == T) return flow;
	int rest = flow, k;
	for (int i = now[x], y; i && rest; i = to[i]) {
		now[x] = i;
		if (w[i] && d[y = v[i]] == d[x] + 1) {
			k = dinic(y, min(rest, w[i]));
			if (!k) d[y] = 0;
			w[i] -= k;
			w[i ^ 1] += k;
			rest -= k;
		}
	}
	return flow - rest;
}
void add_edge(int a, int b, int c) {
	w[++tot] = c, v[tot] = b, to[tot] = h[a], h[a] = tot;
	w[++tot] = 0, v[tot] = a, to[tot] = h[b], h[b] = tot;
}
void add(int a, int b, int c) {
	add_edge(a, b, c), add_edge(b, a, 0);
}
signed main() {
	IOS;
	cin >> n;
	for (int i = 1; i <= n; ++i) cin >> a[i];
	for (int i = 1; i <= n; ++i) cin >> b[i];
	S = 0, T = n + 1;
	int sum = 0;
	for (int i = 1; i <= n; ++i) {
		if (b[i] > 0) add(S, i, b[i]), sum += b[i];
		else add(i, T, -b[i]);
		for (int j = 1; j <= a[i] / j; ++j) {
			int t = j;
			if (a[i] % t == 0 && pos[t]) {
				add(i, pos[t], INF);
				// cout << i << "==>" << pos[t] << "\n";
			}
			t = a[i] / j;
			if (a[i] % t == 0 && t != j && pos[t]) {
				add(i, pos[t], INF);
				// cout << i << "==>" << pos[t] << "\n";
			}
		}
		pos[a[i]] = i;
	}
	while (bfs()) while (flow = dinic(S, INF)) maxflow += flow;
	cout << sum - maxflow << "\n";
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值