【已补完】【解题报告】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;
}
乐子:
最后发现是我 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;
}
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;
}