Description
定义一个连通图的权值为所有顶点点权乘积,定义一个无向图的权值为这个无向图的极大连通子图权值和,现给出一张有n个点的无向图,每个点有点权wi,设删去节点i后此图权值为z[i],求
Input
第一行一整数T表示用例组数,每组用例首先输入两个整数n和m表示点数和边数,之后输入n个整数wi表示每个点的点权,最后m行每行两个整数u个v表示u和v之间有一条边相连
(T<=1000,2<=n<=10^5,1<=m<=2*10^5,1<=wi<=10^9,sigma(n),sigma(m)<=1.5*10^6
Output
对于每组用例,输出一个整数S
Sample Input
1
3 2
1 2 3
1 2
2 3
Sample Output
20
Solution
先对原图用tarjan求一遍点双连通分量,对每个块用一个vector记录其中的点,对每个块新加一个点,重新建图,让这个点连向块内所有边,这样就可以得到一个至多block棵树的森林(block为块数),而且这个森林的连通性与原图连通性一致,从编号大的顶点为根节点对这张图dfs,令mul[i]表示以i节点为根的子树权值(如果i不是新加的点,那么这个子树必然是原图中一连通图),令sum[i]表示以i节点为根的所有子树权值和,为避免新加点对权值的影响,可以令新加的点的点权为1,在dfs过程中进行树形DP可以求出所有的mul和sum值,稍微分析下这棵树和dfs过程会发现以下几点:
1.孤立点的根节点是自身
2.非孤立点的根节点是新加节点
3.割点是非叶子节点
4.非割点都是叶子节点
令cnt为所有根节点的mul值之和
那么对于非孤立点i,删掉它后原图的权值就是mul[root[i]]/mul[i]+sum[i]+cnt-mul[root[i]]
对于孤立点i,删掉它原图的权值就是cnt-mul[i]
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
#define mod 1000000007ll
#define maxn 111111
vector<int>g[2*maxn];
int low[maxn],dfn[maxn],stack[maxn];
int index,top;
int block;//块的数量
int bridge;//桥的数量
bool instack[maxn];
vector<int>vec[maxn];//vec[i]表示第i个块中的点集,i从1~block
void add_edge(int u,int v)
{
g[u].push_back(v),g[v].push_back(u);
}
void Tarjan(int u,int pre)
{
int v;
low[u]=dfn[u]=++index;
stack[top++]=u;
instack[u]=1;
for(int i=0;i<g[u].size();i++)
{
v=g[u][i];
if(v==pre)continue;
if(!dfn[v])
{
Tarjan(v,u);
if(low[u]>low[v])low[u]=low[v];
if(low[v]>dfn[u])bridge++;//找到一个桥
if(low[v]>=dfn[u])//找到一个割点u
{
block++;
vec[block].clear();
int vn;
do
{
vn=stack[--top];
vec[block].push_back(vn);
instack[vn]=0;
}while(vn!=v);
vec[block].push_back(u);//割点可能还属于其他块所以不能出栈
}
}
else if(instack[v]&&low[u]>dfn[v])
low[u]=dfn[v];
}
}
void solve(int n)
{
memset(dfn,0,sizeof(dfn));
memset(instack,0,sizeof(instack));
index=block=top=bridge=0;
for(int i=1;i<=n;i++)
if(!dfn[i])
Tarjan(i,-1);
}
int T,n,m,w[2*maxn],vis[2*maxn],root[2*maxn];
ll mul[2*maxn],sum[2*maxn];
void dfs(int u)
{
vis[u]=1,mul[u]=w[u],sum[u]=0;
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(vis[v])continue;
root[v]=root[u];
dfs(v);
mul[u]=mul[u]*mul[v]%mod;
sum[u]=(sum[u]+mul[v])%mod;
}
}
ll mod_pow(ll a,ll b)
{
a%=mod;
ll ans=1;
while(b)
{
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&w[i]),g[i].clear();
for(int i=0;i<m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add_edge(u,v);
}
solve(n);
for(int i=1;i<=n+block;i++)g[i].clear();
for(int k=1;k<=block;k++)
{
w[n+k]=1;
for(int i=0;i<vec[k].size();i++)
{
int v=vec[k][i];
add_edge(v,n+k);
}
}
memset(vis,0,sizeof(vis));
for(int i=n+block;i;i--)
if(!vis[i])
root[i]=i,dfs(i);
ll cnt=0,ans=0;
for(int i=1;i<=n+block;i++)
if(root[i]==i)cnt=(cnt+mul[i])%mod;
for(int i=1;i<=n;i++)
{
ll temp;
if(root[i]!=i)
{
temp=mul[root[i]]*mod_pow(mul[i],mod-2)%mod;
temp=((temp+sum[i])%mod+(cnt-mul[root[i]]+mod)%mod)%mod;
}
else temp=(cnt-mul[i]+mod)%mod;
ans=(ans+i*temp%mod)%mod;
}
printf("%I64d\n",ans);
}
return 0;
}