比赛链接:21.7.15-NUAA暑期集训
比赛码:NUAAACM20210715
A - 热浪
单源最短路模板。
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
#define INF 0x3f3f3f3f
#define io_speed_up ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
int cnt, head[2510], dis[2510], n, m, s, t;
bool vis[2510];
struct Edge {
int v, w, next;
} edge[12500];
void addEdge(int u, int v, int w) {
edge[++cnt].v = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt;
}
void spfa(int s) {
queue<int> q;
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= n; ++i) {
dis[i] = INF;
}
dis[s] = 0;
vis[s] = true;
q.push(s);
while (!q.empty()) {
int u = q.front();
vis[u] = false;
q.pop();
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].v;
if (dis[v] > dis[u] + edge[i].w) {
dis[v] = dis[u] + edge[i].w;
if (!vis[v]) {
q.push(v);
vis[v] = true;
}
}
}
}
}
int main() {
io_speed_up;
cin >> n >> m >> s >> t;
for (int i = 1, u, v, w; i <= m; ++i) {
cin >> u >> v >> w;
addEdge(u, v, w);
addEdge(v, u, w);
}
spfa(s);
cout << dis[t];
return 0;
}
B - Silver Cow Party
存两张图:
第一张图正常存边,用于求从(x)返回其他点时的最短路。
第二张图存第一张图的反向边,由于从其他点到(x)是多源单终点,存反向边则可以看作是从(x)到其他点,再求一遍最短路。
将各点在两次最短路里的(dis)值相加,更新最大值。
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
#define INF 0x3f3f3f3f
#define io_speed_up ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
int cnt[2], head[2][1010], dis[2][1010], n, m, x;
bool vis[1010];
struct Edge {
int v, w, next;
} edge[2][100010];
void addEdge(int k, int u, int v, int w) {
edge[k][++cnt[k]].v = v;
edge[k][cnt[k]].w = w;
edge[k][cnt[k]].next = head[k][u];
head[k][u] = cnt[k];
}
void spfa(int s, int k) {
queue<int> q;
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= n; ++i) {
dis[k][i] = INF;
}
dis[k][s] = 0;
vis[s] = true;
q.push(s);
while (!q.empty()) {
int u = q.front();
vis[u] = false;
q.pop();
for (int i = head[k][u]; i; i = edge[k][i].next) {
int v = edge[k][i].v;
if (dis[k][v] > dis[k][u] + edge[k][i].w) {
dis[k][v] = dis[k][u] + edge[k][i].w;
if (!vis[v]) {
q.push(v);
vis[v] = true;
}
}
}
}
}
int main() {
io_speed_up;
cin >> n >> m >> x;
for (int i = 1, u, v, w; i <= m; ++i) {
cin >> u >> v >> w;
addEdge(0, u, v, w);
addEdge(1, v, u, w);
}
spfa(x, 0);
spfa(x, 1);
int ans = 0;
for (int i = 1; i <= n; ++i) {
ans = max(ans, dis[0][i] + dis[1][i]);
}
cout << ans << endl;
return 0;
}
C - Intervals
该题是一个差分约束系统。
设(f[i])表示在([0,i])区间内取了多少个数。
由输入可以得到多个不等式(f[b] - f[a - 1] geq c),则在建图过程中点(a-1)有一条权值为(c)的有向边到点(b)。
同时题目本身存在隐藏限制条件(1geq f[i] - f[i-1] geq 0(0 leq i leq maxn)),即(left{egin{matrix}f[i] - f[i-1] geq 0 \f[i-1] - f[i] geq -1end{matrix}
ight.),则在建图过程中每个点(i-1)有一条权值为(0)的有向边到点(i),点(i)有一条权值为(-1)的有向边到点(i-1)。
由于区间左端最小可以为(0),所以需要将数组下标加(1),防止出现下标为(-1)的情况。
建图完成之后跑最长路即可。
#include <iostream>
#include <queue>
using namespace std;
#define io_speed_up ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
struct Edge {
int v, w, next;
} edge[5000010];
int n, a, b, c, cnt, head[50005], dis[50005], maxb;
bool vis[50005];
void addEdge(int u, int v, int w) {
edge[++cnt].v = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt;
}
void spfa(int s) {
queue<int> q;
for (int i = 0; i <= maxb + 1; ++i) {
dis[i] = -1;
}
dis[s] = 0;
vis[s] = true;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
vis[u] = false;
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].v;
if (dis[v] < dis[u] + edge[i].w) {
dis[v] = dis[u] + edge[i].w;
if (!vis[v]) {
q.push(v);
vis[v] = true;
}
}
}
}
}
int main() {
io_speed_up;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a >> b >> c;
addEdge(a, b + 1, c);
maxb = maxb >= b ? maxb : b;
}
for (int i = 1; i <= maxb + 1; ++i) {
addEdge(i - 1, i, 0);
addEdge(i, i - 1, -1);
}
spfa(0);
cout << dis[maxb + 1] << endl;
return 0;
}
D - 飞行路线
建立多层图,最多免费(k)次,所以有(k+1)层图,第(i)层表示当前已用(i)次免费机会,加边时在各层的都加上同一条边,一次免费相当于在上下两层的两点之间建立一条权值为(0)的边,起点在第(0)层,对整个多层图跑一遍最短路,一个点的最短距离就是其在各层中的最短距离的最小值。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
#define io_speed_up ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define INF 0x3f3f3f3f
int head[110010], n, m, k, s, t, cnt;
long long dis[110010];
bool vis[110010];
struct Edge {
int v, next;
long long w;
} edge[11000010];
void addEdge(int u, int v, long long w) {
edge[++cnt].v = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt;
}
void spfa(int s) {
queue<int> q;
memset(vis, 0, sizeof(vis));
for (int i = 0; i < k * n + n; ++i) {
dis[i] = INF;
}
dis[s] = 0;
vis[s] = true;
q.push(s);
while (!q.empty()) {
int u = q.front();
vis[u] = false;
q.pop();
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].v;
if (dis[v] > dis[u] + edge[i].w) {
dis[v] = dis[u] + edge[i].w;
if (!vis[v]) {
q.push(v);
vis[v] = true;
}
}
}
}
}
int main() {
io_speed_up;
cin >> n >> m >> k >> s >> t;
for (int i = 1, u, v, w; i <= m; ++i) {
cin >> u >> v >> w;
for (int j = 0; j <= k; ++j) {
int tu = u + n * j, tv = v + n * j;
if (j != k) {
addEdge(tu, tv + n, 0);
addEdge(tv, tu + n, 0);
}
addEdge(tu, tv, w);
addEdge(tv, tu, w);
}
}
spfa(s);
long long ans = INF;
for (int i = 0; i <= k; ++i) {
ans = min(ans, dis[t + n * i]);
}
cout << ans;
return 0;
}
E - Watering Hole
在已有图中添加一个点,表示地下天然水源,该点与其他各点之间建立一条权值为打井费用的边,在某一点打井相当于选择该点与地下水源之间的边,然后跑最小生成树即可。
#include <algorithm>
#include <cstdio>
#include <iostream>
using namespace std;
int n, fa[310], cnt;
struct Edge {
int u, v, w;
bool operator<(const Edge &obj) const { return w < obj.w; }
} edge[45310];
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
int kruscal() {
int ans = 0;
for (int i = 0; i <= n; ++i) {
fa[i] = i;
}
int tot = 0;
for (int i = 1; i <= cnt; ++i) {
int u = edge[i].u, v = edge[i].v;
int fau = find(u), fav = find(v);
if (fau != fav) {
fa[fau] = fav;
tot++;
ans += edge[i].w;
if (tot == n) {
return ans;
}
}
}
}
int main() {
while (scanf("%d", &n) == 1) {
cnt = 0;
for (int i = 1; i <= n; ++i) {
edge[++cnt].u = 0;
edge[cnt].v = i;
scanf("%d", &edge[cnt].w);
}
for (int i = 1, w; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
scanf("%d", &w);
if (i < j) {
edge[++cnt].u = i;
edge[cnt].v = j;
edge[cnt].w = w;
}
}
}
sort(edge + 1, edge + cnt + 1);
printf("%d
", kruscal());
}
return 0;
}
G - The Perfect Stall
二分图匹配模板。
#include <cstring>
#include <iostream>
using namespace std;
#define io_speed_up ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
int n, m, link[210], ans;
bool vis[210], g[210][210];
bool hungry(int now) {
for (int i = 1; i <= n; i++) {
if (!vis[i] && g[now][i]) {
vis[i] = true;
if (!link[i] || hungry(link[i])) {
link[i] = now;
return true;
}
}
}
return false;
}
int main() {
io_speed_up;
while (cin >> n >> m) {
memset(g, 0, sizeof(g));
memset(link, 0, sizeof(link));
for (int i = 1, s, x; i <= n; ++i) {
cin >> s;
while (s--) {
cin >> x;
g[i][x] = true;
}
}
ans = 0;
for (int i = 1; i <= n; ++i) {
memset(vis, 0, sizeof(vis));
ans += hungry(i);
}
cout << ans << endl;
}
return 0;
}
H - COURSES
还是二分图匹配模板。
#include <cstring>
#include <iostream>
#include <cstdio>
using namespace std;
int t, p, n, link[310], ans, cnt, head[310];
bool vis[310];
struct Edge {
int v, next;
} edge[30010];
void addEdge(int u, int v) {
edge[++cnt].v = v;
edge[cnt].next = head[u];
head[u] = cnt;
}
bool hungry(int u) {
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].v;
if (!vis[v]) {
vis[v] = true;
if (!link[v] || hungry(link[v])) {
link[v] = u;
return true;
}
}
}
return false;
}
int main() {
scanf("%d", &t);
while (t--) {
scanf("%d%d", &p, &n);
cnt = 0;
memset(head, 0, sizeof(head));
memset(link, 0, sizeof(link));
for (int i = 1, k, x; i <= p; ++i) {
scanf("%d", &k);
while (k--) {
scanf("%d", &x);
addEdge(i, x);
}
}
ans = 0;
for (int i = 1; i <= p; ++i) {
memset(vis, 0, sizeof(vis));
ans += hungry(i);
}
if (ans == p) {
puts("YES");
} else {
puts("NO");
}
}
return 0;
}