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

来源:互联网 发布:数据审计追踪 编辑:程序博客网 时间:2024/05/22 00:44

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

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

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

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

割点则有两种情况:

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

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

代码:

#include <set>#include <map>#include <queue>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>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;}
0 0