2016 Multi-university-training-contests-2 1006 点双连通分量

来源:互联网 发布:程序员背包 编辑:程序博客网 时间:2024/06/09 22:37

链接

http://acm.hdu.edu.cn/showproblem.php?pid=5739

题意


给一个有n个节点和m条边的无向图,每个点有自己的点权wi,Gi的定义是删除i点形成的子图。
Gi的权值计算方法为
- 若为连通的图,是每个点的点权的乘积。
- 不连通,每个连通图或者孤立点的和。
最后求S=ni=1Gii


解析


首先我想说说ACM-ICPC程序设计系列 图论及应用。模板代码有坑啊,坑的一手好逼。
接下来,说说思路,根据题意,可以判断主要求点双连通分量,求出割点,将不是割点的点进行缩点。之后就形成了一棵树,在树上面进行计算就方便多了。遍历新图,依次遍历割点、缩点和孤立节点,记录它们整个块的权值。
计算。
1、若为割点,但不是根节点,整个连通块权值除以以他为根的子树的权值,然后在加该及节点的每个子树的权值;若是根节点直接加他的子树的权值。
2、若不是割点,直接整个连通块的权值除以该接点的权值。
注意:除法的取模,用费马小定理。
ans=(p/q)modt=pqt2


代码

#include <iostream>#include <cstdio>#include <cstring>#include <vector>#include <algorithm>using namespace std;typedef long long LL;const int maxn = 200000+5;const int mod = 1000000000+7;int n, m;vector<int>p[maxn], V[maxn], E[maxn];int w[maxn], iw[maxn];int low[maxn], dfn[maxn];bool flag[maxn], vis[maxn];int st[maxn];int pre[maxn];int stk, sig, bcc_cnt;int color[maxn];int dp[maxn];void init() {    memset(dfn, 0, sizeof(dfn));    memset(pre, -1, sizeof(pre));    stk = 0;    sig = 0;    bcc_cnt = n;    memset(flag, false, sizeof(flag));    memset(vis, false, sizeof(vis));    memset(color, 0, sizeof(color));}int pow(int a, int b) {    int res = 1;    while (b) {        if (b&1)            res = 1LL*res*a%mod;        a = 1LL*a*a%mod;        b >>= 1;    }    return res;}void tarjan(int now, int pre) {    dfn[now] = low[now] = ++sig;    st[++stk] = now;    int son = 0;    for (int i=0; i<p[now].size(); i++) {        int v = p[now][i];        son++;        if (v == pre)            continue;        if (!dfn[v]) {            tarjan(v, now);            low[now] = min(low[now], low[v]);            if (low[v] >= dfn[now]) { // now不为树根时, low[v] >= dfn[now], 就说明now是割点。                bcc_cnt++;                flag[now] = true;                w[bcc_cnt] = 1;                V[bcc_cnt].clear();                do {                    w[bcc_cnt] = 1LL*w[bcc_cnt]*w[st[stk]]%mod;                    V[bcc_cnt].push_back(st[stk]);                }while (st[stk--] != v);                w[bcc_cnt] = 1LL*w[bcc_cnt]*w[now]%mod;                V[bcc_cnt].push_back(now);            }        }        else            low[now] = min(low[now], dfn[v]);    }    if (pre < 0 && son == 1)  //now为树根,但是它的子树必须超过一个。        flag[now] = false;}void DFS(int u, int p, int fa) {    color[u] = fa;    vis[u] = true;    pre[u] = p;    dp[u] = w[u];    for (int i=0; i<E[u].size(); i++) {        int v = E[u][i];        if (v == p)            continue;        DFS(v, u, fa);        dp[u] = 1LL*dp[u]*dp[v]%mod;    }    if (u > n) {        for (int i=0; i<V[u].size(); i++)        {            vis[V[u][i]] = true;            color[V[u][i]] = fa;        }    }}int main(){    int T;    scanf("%d", &T);    while (T--) {        scanf("%d%d", &n, &m);        init();        for (int i=1; i<=n; i++) {            p[i].clear();            scanf("%d", &w[i]);            iw[i] = pow(w[i], mod-2);        }        for (int i=0; i<m; i++) {            int u, v;            scanf("%d%d", &u, &v);            p[u].push_back(v);            p[v].push_back(u);        }        for (int i=1; i<=n; i++) {            if (!dfn[i])            tarjan(i, -1);        }        for (int i=1; i<=bcc_cnt; i++)            E[i].clear();        for (int i=n+1; i<=bcc_cnt; i++) {            for (int j=0; j<V[i].size(); j++) {                int v = V[i][j];                if (flag[v]) {                    E[i].push_back(v);                    E[v].push_back(i);                    w[i] = 1LL*w[i]*iw[v]%mod;                }            }        }        int ans = 0;        for (int i=1; i<=n; i++) {    //遍历割点            if (!vis[i] && flag[i]) {                DFS(i, -1, i);                ans = (ans+dp[i])%mod;            }        }        for (int i=n+1; i<=bcc_cnt; i++) {  // 遍历没有割点的连通块            if (!vis[i]) {                DFS(i, -1, i);                ans = (ans+dp[i])%mod;            }        }        for (int i=1; i<=n; i++) {  //遍历孤立节点            if (!vis[i]) {                DFS(i, -1, i);                ans = (ans+dp[i])%mod;            }        }        int res = 0;        for (int i=1; i<=n; i++) {            int fa = color[i];            int temp = ans;            ans = (ans-dp[fa]+mod)%mod;            if (flag[i]) {                int t = 1LL*dp[fa]*pow(dp[i], mod-2)%mod;                if (i != fa)                    ans = (ans+t)%mod;                for (int j=0; j<E[i].size(); j++) {                    int v = E[i][j];                    if (pre[v] == i)                        ans = (ans + dp[v])%mod;                }            }            else            {                if (i != fa)                    ans = (ans+1LL*dp[fa]*iw[i]%mod)%mod;            }            res = (res + 1LL*i*ans)%mod;            ans = temp;        }        printf("%d\n", res);    }    return 0;}
0 0