一本通tarjan题目

目录

  • loj
    • 缩点
      • 10091 受欢迎的牛
      • 10093 网络协议
      • 10094 消息的传递
      • 10095 间谍网络
      • 10096 抢掠计划
    • 割点和桥
      • 10098 分离的路径
      • 10099 矿场搭建
      • 10100 网络
      • 10101 嗅探器
      • 10102 旅游航道
      • 10103 电力
      • 10104 Blockade

基本上都是板子, 还没做完

loj

缩点

10091 受欢迎的牛

缩点后出度为$ 0 $的点就是欢迎的牛,超过一个点则不存在

#include 
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 
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 
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
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 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<

割点和桥

10098 分离的路径

无向缩点后必然是一棵树。要求成为一个边双图。
统计树上度数为1的点,即叶节点,$ ans = (leaf + 1) / 2 $ .
证明还请自行百度。

#include 
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
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;
}

你可能感兴趣的:(一本通tarjan题目)