题目大意:给定一个无向图,对于每个连通分量的权值为其中点的权值乘积,而整个无向图的权值为所以连通分量的权值和。Gi表示删除点i,图的权值,求sum(i*Gi)%(10e+7)
显然只有删除割点才会改变图的连通性,但我也没有想到怎么解决,这里根据官方题解
显然, 只要删掉关键点才会使图不联通. 对于其他点, 权值很容易计算.
首先求出所有的点双联通分量, 对于每一个点双联通分量SSS, 新建一个节点sss, 向SSS中每个节点vvv连边. 这样一来, 新增的点和原来图中的点会构成一个森林(据说这个有个名字, block forest data structure). 很容易观察到, 叶子节点肯定都是非关键点, 内部节点要么是关键点, 要么是新增的节点.
对于这个森林FFF, 删掉一个关键点或者一个叶子iii之后, 会得到一个新森林FiF_iFi, 这个FiF_iFi对应的连通块集合和GiG_iGi对应的连通块集合其实是一样的(不考虑那些新增的点). 显然GiG_iGi的权值和FiF_iFi的权值也是一样的, FiF_iFi的权值我们很容易通过树形dp算出来, 那么GiG_iGi的权值也随之而出.
根据对应性质,先求出点双连通分量,再进行树形dp即可。开始求点双连通时用了vector超时,改了就没问题了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <ctime>
using namespace std;
#define ll long long
const int maxn=800005;
const ll mod=1000000007;
ll pri[maxn];
int head[maxn];
int to[maxn],ne[maxn],from[maxn];
int cot;
inline void addedge(int u,int v){
from[cot]=u;
to[cot]=v;
ne[cot]=head[u];
head[u]=cot++;
}
int head1[maxn];
int to1[maxn];
int ne1[maxn];
int cot1;
inline void addEdge(int u,int v){
to1[cot1]=v;
ne1[cot1]=head1[u];
head1[u]=cot1++;
}
ll pow_mod(ll a,ll b){
ll res=1;
while(b){
if(b&1)(res*=a)%=mod;
(a*=a)%=mod;
b>>=1;
}
return res;
}
int dfn[maxn];
int dfs_cot;
int bcc_cot;
int st[maxn];
int top;
bool vis[maxn];
int dfs(int u,int fa){
vis[u]=true;
int lowu=dfn[u]=++dfs_cot;
for(int i=head[u];~i;i=ne[i]){
int v=to[i];
if(!vis[v]){
st[top++]=i;
int lowv=dfs(v,u);
lowu=min(lowv,lowu);
if(lowv>=dfn[u]){
bcc_cot++;
//printf("bcc[%d]:\n",bcc_cot);
for(;;){
if(top==0)break;
int e=st[--top];
//printf("e[%d]:%d %d\n",e,from[e],to[e]);
//if(bcc_id[from[e]]!=bcc_cot)printf("push:%d\n",from[e]);
addEdge(bcc_cot,from[e]);
addEdge(from[e],bcc_cot);
//if(bcc_id[to[e]]!=bcc_cot)printf("push:%d\n",to[e]);
addEdge(bcc_cot,to[e]);
addEdge(to[e],bcc_cot);
pri[bcc_cot]=1;
if(from[e]==u&&to[e]==v)break;
}
}
}
else if(dfn[v]<dfn[u]&&v!=fa){
st[top++]=i;
lowu=min(lowu,dfn[v]);
}
}
return lowu;
}
void init(int n){
bcc_cot=n;
top=0;
cot=0;
dfs_cot=0;
cot1=0;
memset(head1,-1,sizeof(head1));
memset(head,-1,sizeof(head));
memset(vis,0,sizeof(vis));
}
ll sum[maxn];
ll product[maxn];
void dp_dfs(int u,int fa){
vis[u]=true;
sum[u]=0;
product[u]=pri[u];
for(int i=head1[u];~i;i=ne1[i]){
int v=to1[i];
if(v==fa)continue;
if(!vis[v]){
dp_dfs(v,u);
sum[u]=(sum[u]+product[v])%mod;
product[u]=(product[v]*product[u])%mod;
}
}
}
void dp(int u,int fa){
dfn[u]=1;
if(fa!=0){
int tmp=product[fa]*pow_mod(product[u],mod-2)%mod;
sum[u]=(sum[u]+tmp)%mod;
product[u]=product[fa];
}
for(int i=head1[u];~i;i=ne1[i]){
int v=to1[i];
if(!dfn[v]){
dp(v,u);
}
}
}
int main(){
int t,n,m;
int u,v;
scanf("%d",&t);
while(t--){
scanf("%d %d",&n,&m);
init(n);
for(int i=1;i<=n;i++){
scanf("%lld",&pri[i]);
pri[i]%=mod;
}
cot=0;
for(int i=0;i<m;i++){
scanf("%d %d",&u,&v);
addedge(u,v);
addedge(v,u);
}
for(int i=1;i<=n;i++){
if(!vis[i])dfs(i,0);
}
memset(vis,0,sizeof(vis));
memset(dfn,0,sizeof(dfn));
ll ans=0;
for(int i=1;i<=n;i++){
if(!vis[i]){
dp_dfs(i,0);
dp(i,0);
(ans+=product[i])%=mod;
}
}
/*
for(int i=1;i<=bcc_cot;i++){
printf("sum[%d]:%lld\n",i,sum[i]);
printf("pro[%d]:%lld\n",i,product[i]);
}
printf("ans:%lld\n",ans);
*/
ll fans=0;
for(int i=1;i<=n;i++){
(fans+=(i*(ans+sum[i]-product[i]+mod))%mod)%=mod;
}
printf("%lld\n",fans);
}
return 0;
}