学习:Tarjan
模板:(Gym102163B)
/*---------------------------------
*File name: A.cpp
*Creation date: 2020-05-02 16:05
*-------------------------------*/
#include<set>
#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair<int, int>
#define Pque priority_queue
#define loop(c, a, b) for(int c = a; c <= b; ++c)
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
int dfn[maxn], low[maxn], color[maxn], vis[maxn];
int top, cnt, sig, n, m;
struct Edge{
int next;
int v;
bool flag;
}e[maxn << 1];
int head[maxn], num;
vector<PII> G;
stack<int> S;
inline void Add_Edge(int u, int v){
e[++num].v = v;
e[num].next = head[u];
e[num].flag = 0;
head[u] = num;
}
inline void init(){
num = -1;
top = cnt = sig = ans = 0;
G.clear();
while(!S.empty()) S.pop();
memset(d, -1, sizeof(d));
memset(head, -1, sizeof head);
memset(vis, 0, sizeof(vis));
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
memset(color,0,sizeof color);
}
void Tarjan(int u){
vis[u] = 1;
S.push(u);
dfn[u] = low[u] = ++cnt;
for(int i = head[u]; i != -1; i = e[i].next){
if(e[i].flag) continue;
e[i].flag = e[i ^ 1].flag = 1;
int v = e[i].v;
if(!dfn[v]) {
Tarjan(v);
low[u] = min(low[u], low[v]);
}
else if(vis[v]) low[u] = min(low[u], dfn[v]);
}
if(low[u] == dfn[u]){
sig++;
while(!S.empty()){
int pos = S.top(); S.pop();
color[pos] = sig;
vis[pos] = 0;
if(pos == u) break;
}
}
}
int main(){
int t;
scanf("%d", &t);
while(t--){
init();
scanf("%d %d", &n, &m);
for(int i = 1; i <= m; ++i){
int u, v;
scanf("%d %d", &u, &v);
G.pb(PII(u, v));
Add_Edge(u, v);
Add_Edge(v, u);
}
for(int i = 1; i <= n; ++i) if(!dfn[i]) Tarjan(i);
}
return 0;
}
例题:CF427 C.CheckPosts
Input 1:
3
1 2 3
3
1 2
2 3
3 2
Output 1:
3 1
Input 2:
5
2 8 0 6 0
6
1 4
1 3
2 4
3 4
4 5
5 1
Output 2:
8 2
Input 3:
10
1 3 2 2 1 3 1 4 10 10
12
1 2
2 3
3 1
3 4
4 5
5 6
5 7
6 4
7 3
8 9
9 10
10 9
Output 3:
15 6
Input 4:
2
7 91
2
1 2
2 1
Output 4:
7 1
题意:有n个城市,在不同的城市建立检查站都有一定的花费。对于每一个检查站,作用范围是当前检查站所在的连通分量的所有城市,问最少需要多少钱建立检查站就可以覆盖所有城市,且对应的有多少种建立方法。
思路: 依照题意,只要在每个联通分量内最小花费的城市处建立一个检查站,则所有的检查站建立的花费和即是最小的,每个连通分量内最小的花费如果有多个,则在其中任意一个地方建立检查站都是可行的,因此对于第二问,只需要用乘法法则将每个连通分量内与最小花费相等的城市数量相乘即为答案。用一个数组记录每个连通分量的最小花费是多少,用一个vector记录连通分量内的城市坐标,再Tarjan算法结束后遍历所有连通分量,再遍历此连通分量对应的vector,计算出和最小花费相等的城市数量。
AC代码:
#include<set>
#include<iostream>
#include<cmath>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<string>
#include<vector>
#include<cstring>
typedef long long LL;
#define PII pair<int, int>
#define pb push_back
const int maxn = 1e5 + 5;
const LL mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
using namespace std;
int n, m;
LL MinAllCost, Method = 1;
vector<int> Tcnt[maxn];
LL TMinCost[maxn];//联通分量内最小花费point
int S[maxn];//栈
int dfn[maxn], low[maxn];//
int sig, Top = -1, cnt;//连通分量标号,栈顶,dfn
int vis[maxn];
LL cost[maxn];
vector<int> G[maxn];
void Tarjan(int x){
vis[x] = 1;
low[x] = dfn[x] = ++cnt;
S[++Top] = x;
int len = G[x].size();
for(int i = 0; i < len; ++i){
int v = G[x][i];
if(vis[v] == 0) Tarjan(v);
if(vis[v] == 1) low[x] = min(low[x], low[v]);
}
if(dfn[x] == low[x]){
sig++;
//printf("when sig++ : x = %d\n", x);
while(1){
low[S[Top]] = sig;
vis[S[Top]] = -1;
Tcnt[sig].pb(S[Top]);//连通分量内所有点
//printf("%d ", S[Top]);
TMinCost[sig] = min(TMinCost[sig], cost[S[Top]]);
if(S[Top--] == x) break;
}
//printf("\n");
}
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; ++i){
scanf(" %lld", cost + i);
}
scanf("%d", &m);
for(int i = 1; i <= m;++i){
int u, v;
scanf("%d %d", &u, &v);
G[u].pb(v);
}
memset(TMinCost, inf, sizeof TMinCost);
for(int i = 1; i <= n; ++i) if(vis[i] == 0) Tarjan(i);
//printf("Sig = %d\n", sig);
for(int i = 1; i <= sig; ++i){
MinAllCost += TMinCost[i];
int len = Tcnt[i].size();
//printf("sig = %d : len = %d\n", i, len);
LL cnt = 0;
for(int j = 0; j < len; ++j){
if(cost[Tcnt[i][j]] == TMinCost[i]) cnt++;
}
Method = Method * cnt % mod;
}
printf("%lld %lld\n", MinAllCost, Method);
return 0;
}
Tarjan算法模板:(POJ2553)
int sig;//连通分量标号
int Top;//栈顶坐标
int Stack[maxn];//栈
int cnt;//dfn
int dfn[maxn], low[maxn];
vector<int> G[maxn];//存图
int vis[maxn];//标记
int color[maxn];//染色缩点
int degree[maxn];//出度 for poj 2553
void Tarjan(int x){
vis[x] = 1;
Stack[++Top] = x;
low[x] = dfn[x] = ++cnt;
int len = G[x].size();
for(int i = 0; i < len; ++i){
int v = G[x][i];
if(vis[v] == 0) Tarjan(v);
if(vis[v] == 1) low[x] = min(low[x], low[v]);
}
if(dfn[x] == low[x]){
sig++;
while(1){
low[Stack[Top]] = sig;
vis[Stack[Top]] = -1;
color[Stack[Top]] = sig;
if(Stack[Top--] == x) break;
}
}
}
int main(){
int n, m;//n点m边
while(~ scanf("%d", &n) && n){
scanf("%d", &m);
Top = 0;
sig = 0;
cnt = 0;
for(int i = 1; i <= n; ++i) {
G[i].clear();
vis[i] = 0;
color[i] = 0;
dfn[i] = 0;
low[i] = 0;
degree[i] = 0;
Stack[i] = 0;
}
for(int i = 1; i <= m; ++i){
int u, v;
scanf("%d %d", &u, &v);
G[u].pb(v);
}
for(int i = 1; i <= n; ++i){
if(vis[i] == 0) Tarjan(i);
}
//-------------------------------
for(int i = 1; i <= n; ++i){
int len = G[i].size();
for(int j = 0; j < len; ++j){
int v = G[i][j];
if(color[i] != color[v]) degree[color[i]]++;
}
}
vector<int> ans;
for(int i = 1; i <= sig; ++i){
if(degree[i]) continue;
for(int j = 1; j <= n; ++j){
if(color[j] == i) ans.pb(j);
}
}
sort(ans.begin(), ans.end());
for(int i = 0; i < ans.size() - 1; ++i) printf("%d ", ans[i]);
printf("%d\n", ans[ans.size() - 1]);
//--------------poj2553---------
}
return 0;
}