一本通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
%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" << '
' << 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
",1LL * (ans[i] + n - 1) * 1LL * 2);
	return 0;
}
原文地址:https://www.cnblogs.com/skkyk/p/12181894.html