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";
}