警示后人,48->68->100,三个错误
查看原帖
警示后人,48->68->100,三个错误
565040
Conan15楼主2023/5/2 10:13

48分:

记得把 ans 开 long long!!! 记得把 ans 计算过程中的参数强制转化类型 long long!!!

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = 4e5 + 10;

//------------------------圆方树------------------------

int h[N], e[M], ne[M], idx;

void add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

//------------------------圆方树------------------------

vector<int> g[N];   //原图

//------------------------tarjan------------------------

int low[N], dfn[N], tot = 0;
int stk[N], top = 0;
int scc[N], cnt = 0;

//------------------------tarjan------------------------

//------------------------ D  P ------------------------

int num = 0;
int sz[N], val[N];    //子树大小&点权
long long ans = 0;

//------------------------ D  P ------------------------


void tarjan(int u) {    //tarjan并生成圆方树
    low[u] = dfn[u] = ++tot;
    stk[++top] = u; num++;
    for (int i = 0; i < g[u].size(); i++) {
        int v = g[u][i];
        if (!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u], low[v]);
            if (low[v] == dfn[u]) {
                val[++cnt] = 0;
                int x = stk[top];
                while (x != u) {
                    add(x, cnt);
                    add(cnt, x);
                    val[cnt]++;
                    x = stk[--top];
                }
                add(u, cnt);
                add(cnt, u);
                val[cnt]++;
            }
        } else
            low[u] = min(low[u], dfn[v]);
    }
}

int n, m;

void DP(int u, int father) {
    if (u <= n) sz[u] = 1;
    for (int i = h[u]; ~i; i = ne[i]) {
        int v = e[i];
        if (v == father) continue;
        DP(v, u);
        ans += (long long) val[u] * sz[u] * sz[v];
        sz[u] += sz[v];
    }
    ans += (long long) val[u] * sz[u] * (num - sz[u]);
}

int main() {
    memset(h, -1, sizeof h);
    scanf("%d%d", &n, &m); cnt = n;
    for (int i = 1; i <= n; i++) val[i] = -1; //初始化圆点权值为-1
    while (m--) {
        int a, b; scanf("%d%d", &a, &b);
        g[a].push_back(b);
        g[b].push_back(a);
    }
    for (int i = 1; i <= n; i++) {
        if (!dfn[i]) {
            num = 0;
            tarjan(i); top--;   //每次tarjan后栈内剩一个点i
            DP(i, 0);
        }
    }
    printf("%lld\n", 2ll * ans);  //正反路径*2
    return 0;
}

68分:

记得圆方树会新建方点,所以要把 N 开到 2×1052 \times 10^5,我直接顺带把 M 也 ×2\times 2 了。

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10, M = 8e5 + 10;

//------------------------圆方树------------------------

int h[N], e[M], ne[M], idx;

void add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

//------------------------圆方树------------------------

vector<int> g[N];   //原图

//------------------------tarjan------------------------

int low[N], dfn[N], tot = 0;
int stk[N], top = 0;
int scc[N], cnt = 0;

//------------------------tarjan------------------------

//------------------------ D  P ------------------------

int num = 0;
int sz[N], val[N];    //子树大小&点权
long long ans = 0;

//------------------------ D  P ------------------------


void tarjan(int u) {    //tarjan并生成圆方树
    low[u] = dfn[u] = ++tot;
    stk[++top] = u; num++;
    for (int i = 0; i < g[u].size(); i++) {
        int v = g[u][i];
        if (!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u], low[v]);
            if (low[v] == dfn[u]) {
                val[++cnt] = 0;
                int x = stk[top];
                while (x != u) {
                    add(x, cnt);
                    add(cnt, x);
                    val[cnt]++;
                    x = stk[--top];
                }
                add(u, cnt);
                add(cnt, u);
                val[cnt]++;
            }
        } else
            low[u] = min(low[u], dfn[v]);
    }
}

int n, m;

void DP(int u, int father) {
    if (u <= n) sz[u] = 1;
    for (int i = h[u]; ~i; i = ne[i]) {
        int v = e[i];
        if (v == father) continue;
        DP(v, u);
        ans += (long long) val[u] * sz[u] * sz[v];
        sz[u] += sz[v];
    }
    ans += (long long) val[u] * sz[u] * (num - sz[u]);
}

int main() {
    memset(h, -1, sizeof h);
    scanf("%d%d", &n, &m); cnt = n;
    for (int i = 1; i <= n; i++) val[i] = -1; //初始化圆点权值为-1
    while (m--) {
        int a, b; scanf("%d%d", &a, &b);
        g[a].push_back(b);
        g[b].push_back(a);
    }
    for (int i = 1; i <= n; i++) {
        if (!dfn[i]) {
            num = 0;
            tarjan(i); top--;   //每次tarjan后栈内剩一个点i
            DP(i, 0);
        }
    }
    printf("%lld\n", 2ll * ans);  //正反路径*2
    return 0;
}

100分:

你说得对,但是我**是沙*

tarjan内的“退栈”部分,我写成了x != u…………

正解是x != v

即下面代码 44 行有改动

即可AC

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10, M = 8e5 + 10;

//------------------------圆方树------------------------

int h[N], e[M], ne[M], idx;

void add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

//------------------------圆方树------------------------

vector<int> g[N];   //原图

//------------------------tarjan------------------------

int low[N], dfn[N], tot = 0;
int stk[N], top = 0;
int scc[N], cnt = 0;

//------------------------tarjan------------------------

//------------------------ D  P ------------------------

int num = 0;
int sz[N], val[N];    //子树大小&点权
long long ans = 0;

//------------------------ D  P ------------------------


void tarjan(int u) {    //tarjan并生成圆方树
    low[u] = dfn[u] = ++tot;
    stk[++top] = u; num++;
    for (int i = 0; i < g[u].size(); i++) {
        int v = g[u][i];
        if (!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u], low[v]);
            if (low[v] == dfn[u]) {
                val[++cnt] = 0;
                for (int x = 0; x != v; top--) {
                    x = stk[top];
                    add(x, cnt);
                    add(cnt, x);
                    val[cnt]++;
                }
                add(u, cnt);
                add(cnt, u);
                val[cnt]++;
            }
        } else
            low[u] = min(low[u], dfn[v]);
    }
}

int n, m;

void DP(int u, int father) {
    if (u <= n) sz[u] = 1;
    for (int i = h[u]; ~i; i = ne[i]) {
        int v = e[i];
        if (v == father) continue;
        DP(v, u);
        ans += (long long) val[u] * sz[u] * sz[v];
        sz[u] += sz[v];
    }
    ans += (long long) val[u] * sz[u] * (num - sz[u]);
}

int main() {
    memset(h, -1, sizeof h);
    scanf("%d%d", &n, &m); cnt = n;
    for (int i = 1; i <= n; i++) val[i] = -1; //初始化圆点权值为-1
    while (m--) {
        int a, b; scanf("%d%d", &a, &b);
        g[a].push_back(b);
        g[b].push_back(a);
    }
    for (int i = 1; i <= n; i++) {
        if (!dfn[i]) {
            num = 0;
            tarjan(i); top--;   //每次tarjan后栈内剩一个点i
            DP(i, 0);
        }
    }
    printf("%lld\n", 2ll * ans);  //正反路径*2
    return 0;
}
2023/5/2 10:13
加载中...