图论单元检测 2+3

3 steps

首先,如果一个图中有奇环,那么这个图就不是二分图,反之就是二分图。

  • 如果图中有奇环,我们通过观察可以发现任意两个点都可以连接一条边,则答案为 \(\dfrac{n\times (n-1)}{2}-m\)

  • 否则,就是给二分图,如果染白色有 \(x\) 个,染黑色有 \(y\) 个,则可以连接 \(x\times y-m\)

#include <bits/stdc++.h>
using namespace std;

#define int long long
#define PII pair<int, int>
#define _for(i, a, b) for (int i = (a); i <= (b); i++)
#define _pfor(i, a, b) for (int i = (a); i >= (b); i--)

const int N = 2e5 + 5;
int n, m, col[N], cnt[N];
vector<int> G[N];

void dfs(int u, int c) {
	col[u] = c;
	cnt[c]++;
	for (auto v : G[u]) {
		if (col[v] == c) {
			cout << n * (n - 1) / 2 - m << endl;
			exit(0); 
		}
		if (!col[v]) dfs(v, 3 - c);
	}
}

signed main() {
	cin >> n >> m;
	_for(i, 1, m) {
		int u, v;
		cin >> u >> v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	dfs(1, 1);
	cout << cnt[1] * cnt[2] - m << endl;
} 

gps

注意题目:一台 GPS 系统认为这条道路不是从 X 前往 N 的最短路径,而不是 而一台 GPS 系统认为这条道路不是从 1 前往 N 的最短路径,所以需要建出返图,倒着跑最短路。

我们肯定对两台 GPS 系统跑 2 次最短路,每次将所有可能在最短路上的边进行标记。如果一条边它被两台认为是最短路边,边权为 \(0\)。被一台认为是最短路边,边权为 \(1\)。没有被认为是最短路边,边权为 \(2\)

最后再根据上面的边权再跑一次最短路就行了。

#include <bits/stdc++.h>
using namespace std;

#define int long long
#define PII pair<int, int>
#define _for(i, a, b) for (int i = (a); i <= (b); i++)
#define _pfor(i, a, b) for (int i = (a); i >= (b); i--)

const int N = 2e5 + 5;
int n, m, dist[N], st[N], vis[N];
struct node {
	int v, p, q;
};
vector<node> nG[N];

struct edge {
	int v, op;
};
vector<edge> G[N];

struct tt {
	int a, b, p, q;
}ed[N];

void dijstra1(int s) {
	priority_queue<PII, vector<PII>, greater<PII> > qq; 
	memset(dist, 0x3f, sizeof dist);
	memset(st, 0, sizeof st);
	dist[s] = 0;
	qq.push({0, s});
	while (qq.size()) {
		int u = qq.top().second; qq.pop();
		if (st[u]) continue;
		st[u] = 1;
		for (auto e : nG[u]) {
			int v = e.v, p = e.p;
			if (dist[v] > dist[u] + p) {
				dist[v] = dist[u] + p;
				qq.push({dist[v], v});
			}
		}
	}
}

void dijstra2(int s) {
	priority_queue<PII, vector<PII>, greater<PII> > qq; 
	memset(dist, 0x3f, sizeof dist);
	memset(st, 0, sizeof st);
	dist[s] = 0;
	qq.push({0, s});
	while (qq.size()) {
		int u = qq.top().second; qq.pop();
		if (st[u]) continue;
		st[u] = 1;
		for (auto e : nG[u]) {
			int v = e.v, q = e.q;
			if (dist[v] > dist[u] + q) {
				dist[v] = dist[u] + q;
				qq.push({dist[v], v});
			}
		}
	}
}

void dijstra3(int s) {
	priority_queue<PII, vector<PII>, greater<PII> > qq; 
	memset(dist, 0x3f, sizeof dist);
	memset(st, 0, sizeof st);
	dist[s] = 0;
	qq.push({0, s});
	while (qq.size()) {
		int u = qq.top().second; qq.pop();
		if (st[u]) continue;
		st[u] = 1;
		for (auto e : G[u]) {
			int v = e.v, op = e.op, d = 0;
			if (op == 1 || op == 2) d = 1;
			else if (op == 0) d = 2;
			if (dist[v] > dist[u] + d) {
				dist[v] = dist[u] + d;
				qq.push({dist[v], v});
			}
		}
	}
}

signed main() {
	cin >> n >> m;
	_for(i, 1, m) {
		int a, b, p, q;
		cin >> a >> b >> p >> q;
		nG[b].push_back({a, p, q});
		ed[i] = {a, b, p, q};
	}
	dijstra1(n);
	_for(i, 1, m) { // 对p跑最短路
		int a = ed[i].a, b = ed[i].b, p = ed[i].p;
		if (dist[a] == dist[b] + p) {
			G[a].push_back({b, 1});
			vis[i] = 1;
		}
	}
	dijstra2(n); // 对q跑最短路
	_for(i, 1, m) {
		int a = ed[i].a, b = ed[i].b, q = ed[i].q;
		if (dist[a] == dist[b] + q) {
			if (vis[i]) G[a].push_back({b, 3});
			else G[a].push_back({b, 2});
			vis[i] = 1;
		}
	}
	_for(i, 1, m) {
		if (!vis[i]) {
			int a = ed[i].a, b = ed[i].b, q = ed[i].q;
			G[a].push_back({b, 0});
		}
	}
	dijstra3(1);
	cout << dist[n] << endl; 
} 

Transition Game

我们对于每个 \(i\) 建立出一条 \(i\to a_i\) 的边。形成一个图。

如果在第 \(i\) 轮中, 不管先手取什么 \(k\)\(i\) 都能被写在黑板上,则说明 \(i\) 一定在环上。

topsort 可以求出环上点的个数。

#include <bits/stdc++.h>
using namespace std;

#define int long long
#define PII pair<int, int>
#define _for(i, a, b) for (int i = (a); i <= (b); i++)
#define _pfor(i, a, b) for (int i = (a); i >= (b); i--)

const int N = 2e5 + 5;
int n, a[N], din[N], res;
vector<int> G[N]; 
queue<int> q;

signed main() {
	cin >> n;
	_for(i, 1, n) cin >> a[i], G[i].push_back(a[i]), din[a[i]]++;
	_for(i, 1, n) if (!din[i]) q.push(i);
	while (q.size()) {
		int u = q.front(); q.pop();
		res++;
		for (auto v : G[u]) {
			if (--din[v] == 0) q.push(v);
		}
	}
	cout << n - res << endl;
} 

Restoring Road Network

给出两两点之间的最短路,判断是否存在。如果存在,找到这张图所有边的边权之和最小值。


思路:

对于 \((i,j,k)\) 来讲,如果 \(d_{i,j}+d_{j,k}<d_{i,k}\) 就是无解。

同时,如果 \(d_{i,j}+d_{j,k}=d_{i,k}\)\((i,k)\) 这条边可以不要。

#include <bits/stdc++.h>
using namespace std;

#define int long long
#define _for(i, a, b) for (int i = (a); i <= (b); i++)
#define _pfor(i, a, b) for (int i = (a); i >= (b); i--)
const int N = 305;
int n, a[N][N], res;

signed main() {
	cin >> n;
	_for(i, 1, n) _for(j, 1, n) cin >> a[i][j];
	_for(k, 1, n) _for(i, 1, n) _for(j, 1, n) {
		if (a[i][k] + a[k][j] < a[i][j]) puts("-1"), exit(0);
	}
	_for(i, 1, n) _for(j, i + 1, n) {
		int flg = 0;
		_for(k, 1, n) {
			if (k == i || k == j) continue;
			if (a[i][k] + a[k][j] == a[i][j]) {
				flg = 1;
				break;
			}
		}
		if (!flg) res += a[i][j];
	}
	cout << res << endl;
}

Travel by car

这道题是走到一个点可以加满油,因此问题比较简单。

先跑一遍 floyd,求出两点间的最短耗油量 \(d_{i,j}\)

再根据两两点间的耗油量,建立出一个新图。具体的,如果 \(d_{i,j}\leq L\),就意味着如果在 \(i\) 这个点加满油,是可以到达 \(j\) 的,就把新图中的 \((i,j)\) 边权赋值为 \(1\)。否则赋值为正无穷。

再对新图跑一遍最短路,减去一就是答案。(最开始的时候油是满的)

#include <bits/stdc++.h>
using namespace std;

const int N = 505;

#define PII pair<int, int>
#define int long long
#define _for(i, a, b) for (int i = (a); i <= (b); i++)
#define _pfor(i, a, b) for (int i = (a); i >= (b); i--)

int n, m, l, q, dist[N][N];

signed main() {
	memset(dist, 0x3f, sizeof dist);
	cin >> n >> m >> l;
	_for(i, 1, m) {
		int u, v, w;
		cin >> u >> v >> w;
		dist[u][v] = dist[v][u] = w;
	}
	_for(i, 1, n) dist[i][i] = 0;
	_for(k, 1, n) _for(i, 1, n) _for(j, 1, n) dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
	_for(i, 1, n) _for(j, 1, n) {
		if (i == j) dist[i][j] = 0;
		else if (dist[i][j] <= l) dist[i][j] = 1;
		else dist[i][j] = 0x3f3f3f3f3f3f3f3f;
	}
	_for(k, 1, n) _for(i, 1, n) _for(j, 1, n) dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
	cin >> q;
	while (q--) {
		int u, v;
		cin >> u >> v;
		if (dist[u][v] == 0x3f3f3f3f3f3f3f3f) puts("-1");
		else cout << dist[u][v] - 1 << endl; 
	}
}

Edge Deletion

对于一条边 \((i,k)\),如果存在 \(j\) 使得 \(d_{i,j}+d_{j,k}=d_{i,k}\)\((i,k)\) 这条边可以不要。

这是一个我并不知道的 trick。

#include <bits/stdc++.h>
using namespace std;

#define int long long
#define _for(i, a, b) for (int i = (a); i <= (b); i++)
#define _pfor(i, a, b) for (int i = (a); i >= (b); i--)
const int N = 305;
int n, m, dist[N][N], res;

struct edge {
	int u, v, w;
}ed[N * N];

signed main() {
	memset(dist, 0x3f, sizeof dist);
	cin >> n >> m;
	_for(i, 1, n) dist[i][i] = 0;
	_for(i, 1, m) {
		int a, b, c;
		cin >> a >> b >> c;
		dist[a][b] = min(dist[a][b], c);
		dist[b][a] = min(dist[b][a], c);
		ed[i] = {a, b, c};
	}
	_for(k, 1, n) _for(i, 1, n) _for(j, 1, n) dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
	_for(i, 1, m) {
		int flg = 0;
		_for(j, 1, n) {
			if (j == ed[i].u || j == ed[i].v) continue;
			if (dist[ed[i].u][j] + dist[j][ed[i].v] == dist[ed[i].u][ed[i].v]) {
				flg = 1;
				break;
			}
		}
		if (flg) res++;
	}
	cout << res << endl;
}

P2149

先对 \(x1\to y1\) 求一个最短路图,再对 \(x2\to y2\) 求一个最短路图。什么边可以变为最短路图上的边?\(d_{x1,u}+w+d_{v,y1}=d_{x1,y1}\),那么 \((u,v,w)\) 就可以是最短路图上的边。

这两图的公共边,必形成一个联通块。

现在来证明一下:

假设现在这两图的公共路径形成了两个(或多个)联通块,则说明两图都可以从一个联通块到达另一个联通块。

不过由于最短路的性质,两个联通块必定会以最短路的方式连接,最终形成一个联通块,不可能形成两个及以上个联通块。

所以我们已经知道,两图的公共路径会形成一个联通块。


题目说是并行和相遇都算公共,但所求链显然不会又包括并行,又包含相遇,否则因为连通块不可能同时有 \(u\to v\)\(v\to u\) 边,它不是链了。我们可以求一下只保留在两图中同向出现的边时的最长链,和只保留在两图中反向出现的边时的最长链。分别跑 topsort 即可。

#include <bits/stdc++.h>
using namespace std;

#define y1 Y1
#define PII pair<int, int>
#define _for(i, a, b) for (int i = (a); i <= (b); i++)
#define _pfor(i, a, b) for (int i = (a); i >= (b); i--)
#define int long long
const int N = 3e5 + 5;

int n, m, x1, y1, x2, y2, dist[5][N], st[N], din[N], ds[N], res;
vector<PII> G[N], nG[N];
struct node {
	int u, v, w;
}ed[N]; 

void dijstra1(int s) {
	priority_queue<PII, vector<PII>, greater<PII> > q;
	q.push({0, s});
	memset(st, 0, sizeof st);
	dist[0][s] = 0;
	while (q.size()) {
		int u = q.top().second; q.pop();
		if (st[u]) continue;
		st[u] = 1;
		for (auto e : G[u]) {
			int v = e.first, d = e.second;
			if (dist[0][v] > dist[0][u] + d) {
				dist[0][v] = dist[0][u] + d;
				q.push({dist[0][v], v});
			}
		}
	}
}

void dijstra2(int s) {
	priority_queue<PII, vector<PII>, greater<PII> > q;
	q.push({0, s});
	memset(st, 0, sizeof st);
	dist[1][s] = 0;
	while (q.size()) {
		int u = q.top().second; q.pop();
		if (st[u]) continue;
		st[u] = 1;
		for (auto e : G[u]) {
			int v = e.first, d = e.second;
			if (dist[1][v] > dist[1][u] + d) {
				dist[1][v] = dist[1][u] + d;
				q.push({dist[1][v], v});
			}
		}
	}
}

void dijstra3(int s) {
	priority_queue<PII, vector<PII>, greater<PII> > q;
	q.push({0, s});
	memset(st, 0, sizeof st);
	dist[2][s] = 0;
	while (q.size()) {
		int u = q.top().second; q.pop();
		if (st[u]) continue;
		st[u] = 1;
		for (auto e : G[u]) {
			int v = e.first, d = e.second;
			if (dist[2][v] > dist[2][u] + d) {
				dist[2][v] = dist[2][u] + d;
				q.push({dist[2][v], v});
			}
		}
	}
}

void dijstra4(int s) {
	priority_queue<PII, vector<PII>, greater<PII> > q;
	q.push({0, s});
	memset(st, 0, sizeof st);
	dist[3][s] = 0;
	while (q.size()) {
		int u = q.top().second; q.pop();
		if (st[u]) continue;
		st[u] = 1;
		for (auto e : G[u]) {
			int v = e.first, d = e.second;
			if (dist[3][v] > dist[3][u] + d) {
				dist[3][v] = dist[3][u] + d;
				q.push({dist[3][v], v});
			}
		}
	}
}

void topsort() {
	queue<int> q;
	_for(i, 1, n) if (!din[i]) q.push(i);
	memset(ds, 0, sizeof ds);
	while (q.size()) {
		int u = q.front(); q.pop();
		for (auto e : nG[u]) {
			int v = e.first, d = e.second;
			ds[v] = max(ds[v], ds[u] + d);
			res = max(res, ds[v]);
			if (--din[v] == 0) q.push(v);
		}
	}
} 


signed main() {
	memset(dist, 0x3f, sizeof dist);
	cin >> n >> m;
	cin >> x1 >> y1 >> x2 >> y2;
	_for(i, 1, m) {
		int u, v, w;
		cin >> u >> v >> w;
		G[u].push_back({v, w});
		G[v].push_back({u, w});
		ed[i] = {u, v, w};
	}
	dijstra1(x1);
	dijstra2(y1);
	dijstra3(x2);
	dijstra4(y2);
//	puts("2222");
//	cout << dist[3][x2] << endl;
	_for(i, 1, n) {
		for (auto e : G[i]) {
			int j = e.first, w = e.second;
			if (dist[0][i] + w + dist[1][j] == dist[0][y1]) {
				if (dist[2][i] + w + dist[3][j] == dist[2][y2]) {
					nG[i].push_back({j, w});
					din[j]++;
				}
			}
		}
	}
	topsort();
	memset(din, 0, sizeof din);
	_for(i, 1, n) nG[i].clear();
	_for(i, 1, n) {
		for (auto e : G[i]) {
			int j = e.first, w = e.second;
			if (dist[0][i] + w + dist[1][j] == dist[0][y1]) {
				if (dist[3][i] + w + dist[2][j] == dist[2][y2]) {
					nG[i].push_back({j, w});
					din[j]++;
				}
			}
		}
	}
	topsort();
	cout << res << endl;
}
posted @ 2024-04-07 16:22  Otue  阅读(2)  评论(0编辑  收藏  举报