一本通tarjan题目
目录
基本上都是板子, 还没做完
loj
缩点
10091 受欢迎的牛
缩点后出度为$ 0 $的点就是欢迎的牛,超过一个点则不存在
#include <bits/stdc++.h>
using namespace std;
const int N = 10000 + 50;
const int M = 50000 + 50;
struct node {
int next, to;
} edge[M];
int cnt, head[N];
inline void add(int from, int to) { edge[++cnt] = (node){ head[from], to }, head[from] = cnt; }
int n, m, col, top, tot;
int cd[N], color[N], Stack[N], low[N], dfn[N], visited[N], sum[N];
// namespace newgraph{
// int cnt, head[N];
// struct node{ int next, to; }edge[M];
// inline void add(int from, int to) { edge[++cnt] = (node){head[from], to}, head[from] = cnt;} }
//}
void tarjan(int u) {
Stack[++top] = u, dfn[u] = low[u] = ++tot, visited[u] = 1;
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if (!dfn[v])
tarjan(v), low[u] = min(low[u], low[v]);
else if (visited[v])
low[u] = min(low[u], dfn[v]);
}
if (low[u] == dfn[u]) {
color[u] = ++col, sum[col]++, visited[u] = 0;
while (Stack[top] != u) {
color[Stack[top]] = col;
sum[col]++;
visited[Stack[top--]] = 0;
}
top--;
}
}
int main() {
cin >> n >> m;
for (int i = 1, u, v; i <= m; i++) scanf("%d%d", &u, &v), add(u, v);
for (int i = 1; i <= n; i++)
if (!dfn[i])
tarjan(i);
for (int i = 1; i <= n; i++)
for (int j = head[i]; j; j = edge[j].next)
if (color[i] != color[edge[j].to])
cd[color[i]]++;
int ans = 0, sky;
for (int i = 1; i <= col; i++)
if (!cd[i])
ans++, sky = i;
if (ans == 1)
cout << sum[sky];
else
puts("0");
return 0;
}
10093 网络协议
缩点后入度为零的点为第一问,$ max(rd, cd) $ 为第二问。
原因大概就是 形成的 $ DAG $ , 是由几条单向的链合成的, 每条链都要收尾连成环, 故取max
10094 消息的传递
模板
#include <bits/stdc++.h>
using namespace std;
const int N = 1100;
const int M = N * N;
struct node {
int next, to;
} edge[M];
int cnt, head[N];
inline void add(int from, int to) { edge[++cnt] = (node){ head[from], to }, head[from] = cnt; }
int dfn[N], low[N], visited[N], Stack[N], color[N], rd[N];
int tot, top, col;
int n, ans;
void tarjan(int u) {
dfn[u] = low[u] = ++tot, Stack[++top] = u, visited[u] = 1;
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if (!dfn[v])
tarjan(v), low[u] = min(low[u], low[v]);
else if (visited[v])
low[u] = min(low[u], dfn[v]);
}
if (low[u] == dfn[u]) {
color[u] = ++col, visited[u] = 0;
while (Stack[top] != u) {
color[Stack[top]] = col;
visited[Stack[top--]] = 0;
}
top--;
}
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
int x;
scanf("%d", &x);
if (x)
add(i, j);
}
}
for (int i = 1; i <= n; i++)
if (!dfn[i])
tarjan(i);
for (int i = 1; i <= n; i++)
for (int j = head[i]; j; j = edge[j].next)
if (color[i] != color[edge[j].to])
rd[color[edge[j].to]]++;
for (int i = 1; i <= col; i++)
if (!rd[i])
ans++;
cout << ans;
return 0;
}
10095 间谍网络
缩点后入度为零的点才可以收买,但判断是否有解时神经病了,看的题解。
#include <bits/stdc++.h>
using namespace std;
const int N = 3e3 + 50;
const int M = 8e3 + 50;
const int inf = 1e9 + 7;
struct node {
int next, to;
} edge[M];
int cnt, head[N];
inline void add(int from, int to) { edge[++cnt] = (node){ head[from], to }, head[from] = cnt; }
int n, p, r;
int cost[N], bribe[N], dfn[N], low[N], color[N], Stack[N], visited[N], rd[N];
int tot, top, col;
void tarjan(int u) {
Stack[++top] = u, low[u] = dfn[u] = ++tot, visited[u] = 1;
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if (!dfn[v])
tarjan(v), low[u] = min(low[u], low[v]);
else if (visited[v])
low[u] = min(low[u], dfn[v]);
}
if (low[u] == dfn[u]) {
color[u] = ++col, visited[u] = 0;
bribe[col] = cost[u];
while (Stack[top] != u) {
color[Stack[top]] = col;
bribe[col] = min(bribe[col], cost[Stack[top]]);
visited[Stack[top--]] = 0;
}
top--;
}
}
int main() {
cin >> n >> p;
for (int i = 1; i <= n; i++) cost[i] = bribe[i] = inf;
for (int i = 1; i <= p; i++) {
int a, b;
scanf("%d%d", &a, &b);
cost[a] = b;
}
cin >> r;
for (int i = 1; i <= r; i++) {
int u, v;
scanf("%d%d", &u, &v);
add(u, v);
}
for (int i = 1; i <= n; i++)
if (!dfn[i] && cost[i] != inf)
tarjan(i);
for (int i = 1; i <= n; i++)
if (!dfn[i])
return printf("NO\n%d", i), 0;
for (int i = 1; i <= n; i++)
for (int j = head[i]; j; j = edge[j].next)
if (color[i] != color[edge[j].to])
rd[color[edge[j].to]]++;
int ans = 0;
for (int i = 1; i <= col; i++)
if (!rd[i])
ans += bribe[i];
cout << "YES" << '\n' << ans;
return 0;
}
10096 抢掠计划
缩点,建造新图,跑最长路,中途记录答案
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+50;
const int M = 5e5+50;
struct node{ int next, to; }edge[M];
int cnt, head[N];
inline void add(int from, int to) { edge[++cnt] = (node) {head[from], to}, head[from] = cnt; }
int dfn[N], low[N], visited[N], Stack[N], color[N], rd[N], sum[N], cost[N], bar[N], new_bar[N];
int tot, top, col;
int n, m, ans, s, p;
void tarjan(int u) {
dfn[u] = low[u] = ++tot, Stack[++top] = u, visited[u] = 1;
for(int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if(!dfn[v]) tarjan(v), low[u] = min(low[u], low[v]);
else if(visited[v]) low[u] = min(low[u], dfn[v]);
}
if(low[u] == dfn[u]) {
color[u] = ++col, visited[u] = 0;
sum[col] = cost[u], new_bar[col] = bar[u];
while(Stack[top] != u) {
int t = Stack[top];
color[t] = col;
sum[col] += cost[t];
visited[t] = 0;
new_bar[col] += bar[t];
top--;
}
top--;
}
}
namespace new_graph{
struct node{ int next, to; }edge[M];
int cnt, head[N];
inline void add(int from, int to)
{ edge[++cnt] = (node) {head[from], to}, head[from] = cnt; }
int dis[N];
queue<int> q;
void spfa() {
q.push(color[s]); dis[color[s]] = sum[color[s]];
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if(dis[v] < dis[u] + sum[v]) {
dis[v] = dis[u] + sum[v];
if(new_bar[v]) ans = max(ans, dis[v]);
q.push(v);
}
}
}
}
}
int main() {
cin>>n>>m;
for(int i = 1, a, b; i <= m; i++) scanf("%d%d", &a, &b), add(a, b);
for(int i = 1; i <= n; i++) scanf("%d",&cost[i]);
cin>>s>>p;
for(int i = 1, x; i <= p; i++) scanf("%d",&x), bar[x] = 1;
for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i);
for(int i = 1; i <= n; i++)
for(int j = head[i]; j; j = edge[j].next)
if(color[i] != color[edge[j].to]) new_graph::add(color[i], color[edge[j].to]);
new_graph::spfa();
cout<<ans;
return 0;
}
割点和桥
10098 分离的路径
无向缩点后必然是一棵树。要求成为一个边双图。
统计树上度数为1的点,即叶节点,$ ans = (leaf + 1) / 2 $ .
证明还请自行百度。
#include <bits/stdc++.h>
using namespace std;
const int N = 5e3 + 50;
const int M = 1e4 + 50;
struct node {
int next, to;
} edge[M << 1];
int cnt = 1, head[N];
inline void add(int from, int to) { edge[++cnt] = (node){ head[from], to }, head[from] = cnt; }
int tot, col, top;
int dfn[N], low[N], Stack[N], color[N], rd[N];
int n, m, ans;
void tarjan(int u, int pre) {
dfn[u] = low[u] = ++tot, Stack[++top] = u;
for (int i = head[u]; i; i = edge[i].next) {
if (i == (pre ^ 1))
continue;
int v = edge[i].to;
if (!dfn[v])
tarjan(v, i), low[u] = min(low[u], low[v]);
else
low[u] = min(low[u], dfn[v]);
}
if (low[u] == dfn[u]) {
color[u] = ++col;
while (Stack[top] != u) color[Stack[top--]] = col;
top--;
}
}
int main() {
cin >> n >> m;
for (int i = 1, a, b; i <= m; i++) {
scanf("%d%d", &a, &b);
add(a, b), add(b, a);
}
tarjan(1, 0);
for (int i = 1; i <= n; i++)
for (int j = head[i]; j; j = edge[j].next)
if (color[edge[j].to] != color[i])
rd[color[edge[j].to]]++, rd[color[i]]++;
for (int i = 1; i <= col; i++)
if (rd[i] == 2)
ans++;
cout << (ans + 1) / 2;
return 0;
}
10099 矿场搭建
10100 网络
板子不说了
10101 嗅探器
$ n <= 100$ ,暴力就能过
正解建议看luogu题解
10102 旅游航道
割边板子不说了
10103 电力
题解here
10104 Blockade
很好的一道题目。
对于非割点,删掉后产生的贡献为$ 2 * (n - 1) \(
对于割点,就是满足\) low >= dfn $ 的子树 $ size $ 相乘起来;同时搜索树的父节点也会形成一部分,即$ (n - sum - 1) * sum $
更详细的解释可以看洛谷题解。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+20;
const int M = 5e5+50;
struct node{ int next, to;} edge[M<<1];
int cnt, head[N];
inline void add(int from, int to) {edge[++cnt] = (node) {head[from], to}, head[from] = cnt; }
int low[N], size[N], dfn[N], tot, n, m;
long long ans[N];
void tarjan(int u) {
low[u] = dfn[u] = ++tot,size[u] = 1; int sum = 0;
for(int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if(!dfn[v]) {
tarjan(v),low[u] = min(low[u], low[v]);
size[u] += size[v];
if(low[v] >= dfn[u]) {
ans[u] += 1LL * sum * size[v];
sum += size[v];
}
}else low[u] = min(low[u], dfn[v]);
}
ans[u] += 1LL* sum * (n - sum - 1);
}
int main() {
cin>>n>>m;
for(int i = 1, a, b; i <= m; i++) scanf("%d%d", &a, &b), add(a, b), add(b, a);
for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i);
for(int i = 1; i <= n; i++) printf("%lld\n",1LL * (ans[i] + n - 1) * 1LL * 2);
return 0;
}
我就是我不一样的验货