2016多校联合第二场 HDU5739 Fantasia 解题报告

这道题当时赛上过的队比较少读了一遍就没怎么管了。赛后仔细想了想感觉并不是特别难做,tarjan的时候对割点进行处理就行了,写的时候才发现细节巨多堪比模拟。

题目大意:对于一个带点权的图,定义每个连通分量对图权重的贡献为分量里所有点权的乘积,不同分量之间就求和是整个图总权值。又定义 zi 为删去 i 点后图的权值,求 S=(ni=1izi)mod(109+7)

思路:先预处理出每个连通分量的权值以及原图的总权值 sum ,接下来跑一遍tarjan求割点算法。

如果一个点不是割点直接将其从该连通分量的权值中除去,顺便注意孤立点的处理。

割点则有两种情况:

1. 根节点:统计所有子树的权值和,将该连通分量的权值更新;

2. 非根节点:统计所有满足 low[v]dfn[u] 子树的权值和与权值积,前者用于对答案的贡献,后者为了计算出父节点所在连通分量的权值。

代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxn = 1e5 + 15;
const int maxm = 1e6 + 15;
const int inf = 0x3f3f3f3f;
typedef long long ll;
const ll mod = 1e9 + 7;

struct Edge {
    int to, next;
} edge[maxm];

int ecnt, head[maxn];

ll quickPow(ll x, ll y) {
    ll res = 1;
    while(y) {
        if(y & 1) res = res * x % mod;
        y >>= 1; x = x * x % mod;
    }
    return res;
}

ll inv(ll x) {
    return quickPow(x, mod - 2);
}

void add(int u, int v) {
    edge[ecnt].to = v;
    edge[ecnt].next = head[u];
    head[u] = ecnt++;
}

ll w[maxn], ans[maxn], val[maxn], Sum;
int dfn[maxn], low[maxn], cnt, vis[maxn];

void init(int n) {
    Sum = cnt = ecnt = 0;
    memset(vis, 0, sizeof(int) * (n + 1));
    memset(dfn, 0, sizeof(int) * (n + 1));
    memset(head, -1, sizeof(int) * (n + 1));
}

void dfs(int u) {
    vis[u] = 1; val[u] = w[u];
    for(int i = head[u]; ~i; i = edge[i].next) {
        int v = edge[i].to;
        if(vis[v]) continue; dfs(v);
        val[u] = val[u] * val[v] % mod;     
    }
}

inline void add(ll &x, ll y) {
    x += y;
    if(x >= mod) 
        x -= mod;
}

ll tarjan(int fa, int u, int root) {
    ll res = w[u], sum = 0, pro = w[u];
    dfn[u] = low[u] = ++cnt; int chd = 0, fg = 0;
    for(int i = head[u]; ~i; i = edge[i].next) {
        int v = edge[i].to;
        if(!dfn[v]) {
            chd++; ll tmp = tarjan(u, v, root);         
            low[u] = min(low[u], low[v]);       
            if(!fa) add(sum, tmp);
            if(!fa && chd > 1) fg = 1;
            if(fa && low[v] >= dfn[u]) {
                add(sum, tmp);
                pro = pro * tmp % mod;
                fg = 1;
            }
            res = res * tmp % mod;
        }
        else if(v != fa)
            low[u] = min(low[u], dfn[v]);
    }
    if(!fg) {
        // 孤立点
        if(!fa and head[u] == -1) {ans[u] = (Sum - val[root] + mod) % mod; }
        else ans[u] = ((Sum - val[root] + mod) % mod + val[root] * inv(w[u]) % mod) % mod;
    }
    else {
        ll pre = val[root] * inv(pro) % mod;
        if(fa) add(sum, pre);
        ans[u] = (Sum - val[root] + mod) % mod;
        add(ans[u], sum);
    }
    return res;
}

int vec[maxn];

inline int read() {
    char c = getchar();
    while(!isdigit(c)) c = getchar();
    int x = 0;
    while(isdigit(c)) {
        x = x * 10 + c - '0';
        c = getchar();
    }
    return x;
} 

int main() {
    int t; scanf("%d", &t);
    while(t--) {
        int n = read(), m = read(); init(n);
        for(int i = 1; i <= n; i++) w[i] = read();
        for(int i = 1; i <= m; i++) {
            int u = read(), v = read();
            add(u, v); add(v, u);
        }
        int tot = 0;
        for(int i = 1; i <= n; i++) {
            if(vis[i]) continue;
            vec[++tot] = i; dfs(i);
            add(Sum, val[i]);
        }
        for(int i = 1; i <= tot; i++)
            tarjan(0, vec[i], vec[i]);
        ll S = 0;
        for(int i = 1; i <= n; i++)
            add(S, ans[i] * i % mod);
        printf("%lld\n", S);
    }
    return 0;
}

你可能感兴趣的:(图论)