【已补完】【解题报告】THOI暑假图论作业刷题笔记

图论入门大合集

终于到我最喜欢的图论力!*喜

展开目录


P8605 [蓝桥杯 2013 国 AC] 网络寻路

枚举任意两个点作为中间节点时可能的路径。

意识到一组(两个)节点作为中间节点时,两个节点的度 \(-1\) 分别是这组节点首尾可以连接的节点数,二者相乘是这组节点可以提供的路径数,注意这是一个方向的,因为是无向图所以要 \(\times 2\).

当然如果一个节点的度 \(-1\) 小于等于 \(0\) 的话说明这个节点没法构成路径。

展开代码
#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 2 * 1e5 + 5;
int n, m, d[N], u[N], v[N];
ll ans;
int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= m; ++i) scanf("%d%d", u + i, v + i), ++d[u[i]], ++d[v[i]];
	for(int i = 1; i <= m; ++i) ans += 1LL * max(0, d[u[i]] - 1) * max(0, d[v[i]] - 1) * 2;
	printf("%lld\n", ans);
	return 0;
}

P5318 【深基18.例3】查找文献

按题意进行遍历即可。顺便因为我是前向星存图所以排序要用到结构体,但是很明显我不会用结构体前向星。

值得一提的是因为 head[u] 存的是后面的边所以要把 v 较大的边排后面。

展开代码
#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 1e6 + 5;
struct edge {
	int u, to, nxt;
} e[N];
int n, m, u, v, head[N];
bool vis[N];
bool cmp(edge a, edge b) {return a.to > b.to; }
void dfs(int pos) {
	if(vis[pos]) return;
	printf("%d ", pos);
	vis[pos] = 1;
	for(int i = head[pos]; i; i = e[i].nxt) dfs(e[i].to);
}
void bfs() {
	queue<int> q;
	q.push(1);
	printf("1 ");
	vis[1] = 1;
	while(!q.empty()) {
		int f = q.front();
		for(int i = head[f]; i; i = e[i].nxt) {
			int r = e[i].to;
			if(!vis[r]) {q.push(r); printf("%d ", r), vis[r] = 1; }
		}
		q.pop();
	}
}
int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= m; ++i) scanf("%d%d", &u, &v), e[i].u = u, e[i].to = v;
	sort(e + 1, e + 1 + m, cmp);
	for(int i = 1; i <= m; ++i) e[i].nxt = head[e[i].u], head[e[i].u] = i;
	dfs(1);
	putchar('\n');
	memset(vis, 0, sizeof(vis));
	bfs();
	return 0;
}

P1700 [USACO19OPEN] Milk Factory B

因为找的是其他点为起点都能够到达的那个点,所以可以把边反着存,找从一个点出发能够到其他点的那个点。

展开代码
#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 105;
int n, head[N], to[N], nxt[N], tot;
inline void add(int u, int v) {
	to[++tot] = v;
	nxt[tot] = head[u];
	head[u] = tot;
}
bool vis[N];
void dfs(int pos) {
	if(vis[pos]) return;
//	cerr << pos << endl;
	vis[pos] = 1;
	for(int i = head[pos]; i; i = nxt[i]) dfs(to[i]);
}
int main() {
	scanf("%d", &n);
	int u, v;
	for(int i = 1; i < n; ++i) {scanf("%d%d", &u, &v); add(v, u); }
	for(int i = 1; i <= n; ++i) {
		memset(vis, 0, sizeof(vis));
		dfs(i);
		bool flag = 0;
		for(int j = 1; j <= n; ++j) {if(!vis[j]) {flag = 1; break; } }
		if(!flag) {printf("%d\n", i); return 0; }
	}
	puts("-1");
	return 0;
}

每日一乐:

for(int i = head[pos]; i; i = nxt[pos]) dfs(to[i]); 会导致一直遍历同一个点。


B3613 图的存储与出边的排序

调死我了

这题和上面的查找文献基本无差,但是时限比较紧张,需要优化时间复杂度。

热知识:sort(e + 1, e + 1 + n, cmp) 要比直接把数存 priority_queue 里面费时间。

展开代码
#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 1e6 + 5;
long long read(){
	long long x = 0, f = 1;
	char ch = getchar();
	while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar(); }
	while(isdigit(ch)) {x = x * 10 + ch - 48; ch = getchar(); }
	return x * f;
}
int T, n, m, head[N], to[N], nxt[N], tot;
inline void add(int u, int v) {
	to[++tot] = v;
	nxt[tot] = head[u];
	head[u] = tot;
}
priority_queue<int, vector<int>, greater<int> > t;
void dfs(int pos) {
	for(int i = head[pos]; i; i = nxt[i]) t.push(to[i]);
	while(!t.empty()) {
		printf("%d ", t.top());
		t.pop();
	}
}
int main() {
	int u, v;
	T = read();
	while(T--) {
		tot = 0;
		n = read(), m = read();
		for(int i = 1; i <= m; ++i) {u = read(), v = read(); add(u, v); }
		for(int i = 1; i <= n; ++i) {dfs(i); putchar('\n'); }
		for(int i = 1; i <= n; ++i) head[i] = 0;
	}
	return 0;
}

乐子:

image

最后发现是我 head 数组初始化的时候把 \(n\) 打成了 \(m\).


B3643 图的存储

简单板题,按题意储存然后输出即可。

展开代码
#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 1e3 + 5;
int n, m;
bool e[N][N];
vector<int> v[N]; 
int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= m; ++i) {
		int x, y;
		scanf("%d%d", &x, &y);
		e[x][y] = e[y][x] = 1;
		v[x].push_back(y); v[y].push_back(x);
	}
	for(int i = 1; i <= n; ++i) {
		for(int j = 1; j <= n; ++j) printf("%d ", (int)e[i][j]);
		puts("");
	}
	for(int i = 1; i <= n; ++i) {
		printf("%d ", v[i].size());
		sort(v[i].begin(), v[i].end());
		for(int j = 0; j < v[i].size(); ++j) printf("%d ", v[i][j]);
		puts("");
	}
	return 0;
}

image
image
image


P8604 [蓝桥杯 2013 国 C] 危险系数

如果切断一个点时能够切断所有路径,这个点就是关键点。

dfs 的时候会搜出很多不同的路径,类似:

1
| \
2  3
|  |
4  5
| /
6
|
7

这时候搜到7号点后要把6号设成未搜过。

展开代码
#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 4 * 1e3 + 5;
int n, m, head[N], to[N], nxt[N], tot;
int t[N], road, ans;
bool vis[N];
inline void add(int u, int v) {
	to[++tot] = v;
	nxt[tot] = head[u];
	head[u] = tot;
}
void dfs(int u, int v) {
//	cerr << u << " " << t[u] << endl;
	if(u == v) {++road; for(int i = 1; i <= n; ++i) if(vis[i]) ++t[i]; return; }
	if(vis[u]) return;
	vis[u] = 1;
	for(int i = head[u]; i; i = nxt[i]) dfs(to[i], v); 
	vis[u] = 0;
}
int main() {
	scanf("%d%d", &n, &m);
	int u, v;
	for(int i = 1; i <= m; ++i) {scanf("%d%d", &u, &v); add(u, v); add(v, u); }
	scanf("%d%d", &u, &v);
	dfs(u, v);
	if(road) {for(int i = 1; i <= n; ++i) if(t[i] == road) ++ans; }
	else ans = 0;
	printf("%d\n", ans - 1);
	return 0;
}

P10109 [GESP202312 六级] 工作沟通

LCA 的板子。

写初始化的时候把 i >= 0 写成了 i (等同于 i > 0)导致调了半天。

展开代码
#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 1e3 + 5;
int n, m, head[N], to[N], nxt[N], tot;
int t[N], F[N], d[N], f[N][21], l[N];
bool vis[N];
inline void add(int u, int v) {
	to[++tot] = v;
	nxt[tot] = head[u];
	head[u] = tot;
}
void init(ll u, ll fa) {
	d[u] = d[fa] + 1;
	for(int i = 1; i <= l[n]; ++i) {
		if((1 << i) >= d[u]) break;
		f[u][i] = f[f[u][i - 1]][i - 1];
	}
	for(int i = head[u]; i; i = nxt[i]) {
		int v = to[i];
//		cerr << u << " " << v << "回到出生前\n"; 
		if(v == fa) continue;
		f[v][0] = u;
		init(v, u);
	}
}
inline int lca(ll x, ll y) {
	if(d[x] < d[y]) swap(x, y);
	for(int i = l[n]; i >= 0; --i) {/*cerr << x << "置物箱婴儿\n";*/ if(d[f[x][i]] >= d[y]) x = f[x][i]; } 
	if(x == y) return x;
//	cerr << l[n] << "Pink\n"; 
	for(int i = l[n]; i >= 0; --i) {if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i]; /*cerr << x << ":" << f[x][i] << " " << y << ":" << f[y][i] << "Mindbrand\n";*/ }
	return f[x][0];
}
int main() {
	scanf("%d", &n);
	l[0] = -1;
	int u, v;
	for(int i = 1; i < n; ++i) {
		scanf("%d", &v);
		add(v, i); /*add(i, v);*/
		l[i] = l[i >> 1] + 1;
	}
//	cerr << "\n重新分娩\n"; 
	l[n] = l[n >> 1] + 1;
	init(0, 0);
	scanf("%d", &m);
	while(m--) {
		scanf("%d%d", &u, &v);
		int fa = v;
		for(int i = 1; i < u; ++i) {scanf("%d", &v), fa = lca(fa, v); /*cerr << fa << endl;*/ }
		v = fa;
		while(v) v = f[v][0], fa = max(fa, v);
		printf("%d\n", fa);
	}
	return 0;
} 

真是含土量超高的代码啊


P6867 [COCI2019-2020#5] Politicari

\(35pts\) 很好拿,另外 \(65ts\) 想到会重复循环就很好解。

展开代码
#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 2 * 1e3 + 5;
ll n, m, head[N], to[N], nxt[N], tot;
ll a[N][N], vis[N][N];
//inline void add(int u, int v) {
//	to[++tot] = v;
//	nxt[tot] = head[u];
//	head[u] = tot;
//}
void init(ll u, ll v, ll k) {
	if(vis[u][v]) m = (m - k) % (k - vis[u][v]) + k;
	if(k >= m) {printf("%lld\n", v); return; }
	vis[u][v] = k;
	init(v, a[v][u], ++k);
}
int main() {
	scanf("%lld%lld", &n, &m);
//	add(1, 2);
	for(int i = 1; i <= n; ++i) for(int j = 1; j <= n; ++j) scanf("%lld", a[i] + j);
	if(m == 1) {puts("1"); return 0; }
	init(1, 2, 2);
	return 0;
}

后记

image

posted @ 2024-07-24 15:04  _Kiichi  阅读(32)  评论(1编辑  收藏  举报