难题记录1

题目传送门

题目大意:

    有一个n个点m条边的无向图,一共Q次询问,每次询问一个x,要
求回答:若去掉编号为x的边,会有多少个点不能互相到达?
(结果mod 1000)

解题思路

  1. tarjan算法:
    (1) 求出桥,并记录下来
    (2) 求每个点所在的连通块的个数
    (3) 求每个点在dfs树中有多少个子节点
    (4) 求原本有多少个点不能互相到达 (这里标记为k)
  2.求答案 :  
    (1) 判断是否为桥
    (2) 若为桥:求出此边把所在的dfs树分成的两块的节点个数(记为a, b) 答案为a * b + k
    (3) 若不是:答案为k

代码环节:

#include <cstdio>
#include <iostream>
#include <iostream>
using namespace std;

const int maxn = 1000010 ;
int mod = 2000;
struct node {
    int from, to, nxt;
} edge[maxn << 1];
int n, m, q, k, t, cnt = 1, cmp;
int dfn[maxn], low[maxn], vis[maxn], num[maxn], sum[maxn], first[maxn], bridge[maxn];

void add (int u, int v) {
    cnt ++;
    edge[cnt].to = v;
    edge[cnt].from = u;
    edge[cnt].nxt = first[u];
    first[u] = cnt;
}

void tarjan (int u, int com) {
    t ++; cmp ++;
    num[u] = 1;
    dfn[u] = low[u] = t;
    for (int i = first[u]; i; i = edge[i].nxt) {
        int v = edge[i].to;
        if (i == (com ^ 1)) continue;
        if (!dfn[v]) {
            tarjan (v, i);
            low[u] = min (low[u], low[v]);
            num[u] += num[v];
            if (low[v] > dfn[u]) {
                bridge[i / 2 - 1] = 1;
            }
        } else {
            low[u] = min (low[u], dfn[v]);
        }
    }
}

void dfs (int now) {
    sum[now] = cmp;
    vis[now] = 1;
    for (int i = first[now]; i; i = edge[i].nxt) {
        int use = edge[i].to;
        if (!vis[use]) dfs (use);
    }
}

int main() {
    int num1 = 0, num2 = 0;
    scanf ("%d%d%d", &n, &m, &q);
    for (int i = 1; i <= m; ++i) {
        int u, v; scanf ("%d%d", &u, &v);
        add (u, v); add (v, u);
    }
    for (int i = 0; i < n; ++i) {
        cmp = 0;
        if (!dfn[i]) {
            tarjan (i, 0);
            dfs (i);
        }
        cmp %= mod;
        num1 = (num1 + cmp) % mod;
        num2 = (num2 + (cmp * cmp) % mod) % mod;
    }
    k = ((num1 * num1) % mod - num2 % mod + mod) % mod / 2;
    mod = 1000;
    while (q --) {
        int x; scanf ("%d", &x);
        if (!bridge[x]) {
            printf ("%d\n", k);
            continue;
        }
        int a = min (num[edge[(x + 1) * 2].from], num[edge[(x + 1) * 2].to]);
        int b = sum[edge[(x + 1) * 2].to];
        int ans = ((a % mod) * ((b - a) % mod)) % mod;
        printf ("%d\n", (ans + k) % mod);
    }
    return 0;
}
posted @ 2021-01-26 22:28  wangzhongyuan  阅读(3)  评论(0编辑  收藏  举报  来源