这道题当时赛上过的队比较少读了一遍就没怎么管了。赛后仔细想了想感觉并不是特别难做,tarjan的时候对割点进行处理就行了,写的时候才发现细节巨多堪比模拟。
题目大意:对于一个带点权的图,定义每个连通分量对图权重的贡献为分量里所有点权的乘积,不同分量之间就求和是整个图总权值。又定义
zi
为删去
i
点后图的权值,求S=(∑ni=1i⋅zi)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;
}