Hdu-5739 Fantasia (图论点双连通分量+DP)

185 篇文章 0 订阅
116 篇文章 0 订阅
Problem Description
Professor Zhang has an undirected graph G with n vertices and m edges. Each vertex is attached with a weight wi . Let Gi be the graph after deleting the i -th vertex from graph G . Professor Zhang wants to find the weight of G1,G2,...,Gn .

The weight of a graph G is defined as follows:

1. If G is connected, then the weight of G is the product of the weight of each vertex in G .
2. Otherwise, the weight of G is the sum of the weight of all the connected components of G .

A connected component of an undirected graph G is a subgraph in which any two vertices are connected to each other by paths, and which is connected to no additional vertices in G .
 

Input
There are multiple test cases. The first line of input contains an integer T , indicating the number of test cases. For each test case:

The first line contains two integers n and m (2n105,1m2×105) -- the number of vertices and the number of edges.

The second line contains n integers w1,w2,...,wn (1wi109) , denoting the weight of each vertex.

In the next m lines, each contains two integers xi and yi (1xi,yin,xiyi) , denoting an undirected edge.

There are at most 1000 test cases and n,m1.5×106 .
 

Output
For each test case, output an integer S=(i=1nizi) mod (109+7) , where zi is the weight of Gi .
 

Sample Input
  
  
1 3 2 1 2 3 1 2 2 3
 

Sample Output
  
  
20
 

Author
zimpha
 

Source
2016 Multi-University Training Contest 2

题意:给你一个图,定义一个联通块的权值为其所有元素的乘积,一个图的权值为所有联通块权值的和,Gi为G删除i后的子图, 问你所有Gi*i的和。

分析:删除后影响图连通性的点一定是割点,在求出所有双联通分量后重新构图,每个联通分量添加一个虚点,把连通分量的每个点向虚点连边,然后我们就得到了一个和原图连通性一样的森林,在每棵树上DP即可。
 

#include <cstdio>
#include <queue>    
#include <vector>    
#include <cstdio>    
#include <utility>    
#include <cstring>    
#include <iostream>    
#include <algorithm>    
#define INF 0x3f3f3f3f
#define MOD 1000000007
using namespace std;
bool jud[200005],iscut[200005];
int Deep,T,n,m,col,num,dfs_clock,top,bcc_cnt,low[200005],pre[200005],f[200005],bccno[200005];
long long TOT,w[200005],weight[200005],weight2[200005],ans[200005],Count[200005];
vector <int> a[200005],b[200005],bcc[200005];
struct Edge
{
	int u,v;
	Edge()
	{
		u = v = 0;
	}
	Edge(int x,int y)
	{
		u = x,v = y;
	}
} S[200005];
long long ksm(long long a,long long b)
{
	long long ans = 1ll;
	while(b)
	{
		if(b & 1) ans = ans * a % MOD;
		a = a * a % MOD;
		b>>=1; 
	}
	return ans;
}
int dfs(int u,int fa,const int col,const int deep)
{
	Deep = max(Deep,deep);
	low[u] = pre[u] = ++dfs_clock;
	int child = 0;
	f[u] = col;
	Count[col]++;
	weight[col] = weight[col] * w[u] % MOD;
	for(int v : a[u])
	{
		if(!pre[v])
		{
			S[++top] = Edge(u,v);
			child++;
			dfs(v,u,col,deep+1);
			low[u] = min(low[u],low[v]);
			if(low[v] >= pre[u])
			{
				iscut[u] = true;
				bcc[++bcc_cnt].clear();
				for(;;)
				{
					Edge x = S[top--];
					if(bccno[x.u] != bcc_cnt) 
					{
						bcc[bcc_cnt].push_back(x.u);
						bccno[x.u] = bcc_cnt;
					}
					if(bccno[x.v] != bcc_cnt)
					{
						bcc[bcc_cnt].push_back(x.v);
						bccno[x.v] = bcc_cnt; 
					}
					if(x.u == u && x.v == v) break;	
				}	
			}	
		} 
		else 
		 if(pre[v] < pre[u] && v != fa)
		 {
		 	S[++top] = Edge(u,v);
			low[u] = min(low[u],pre[v]);
		 }
	}
	if(fa < 0 && child == 1) iscut[u] = 0;
	return low[u];
}
void dfs2(int u,int fa)
{
	jud[u] = true;
	weight2[u] = w[u];
	long long son = 0;
	for(int v : b[u])
	{
		if(!jud[v]) 
		{
			dfs2(v,u);
			weight2[u] = weight2[u] * weight2[v] % MOD;
			son = (son + weight2[v]) % MOD;
		}
	}
	if(fa == -1 && iscut[u]) ans[u] = (TOT + MOD - weight[f[u]] + son) % MOD;
	else 
	 if(iscut[u]) ans[u] = (TOT + MOD - weight[f[u]] + weight[f[u]]*ksm(weight2[u],MOD-2) % MOD + son) % MOD; 
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		memset(jud,0,sizeof(jud));
		memset(pre,0,sizeof(pre));
		memset(iscut,0,sizeof(iscut));
		memset(bccno,0,sizeof(bccno));
		memset(Count,0,sizeof(Count));
		TOT = col = bcc_cnt = 0;
		scanf("%d%d",&n,&m);
		for(int i = 1;i <= n;i++) 
		{
			scanf("%d",&w[i]);
			a[i].clear();
			b[i].clear();
		}
		for(int i = n+1;i <= 2*n;i++) b[i].clear();
		for(int i = 1;i <= m;i++)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			a[x].push_back(y);
			a[y].push_back(x);
		}
		for(int i = 1;i <= n;i++)
		 if(!pre[i]) 
		 {
		 	dfs_clock = 0;
		 	weight[++col] = 1ll;
		 	dfs(i,-1,col,1);
		 	TOT = (TOT + weight[col]) % MOD;
		 }
		for(int i = 1;i <= bcc_cnt;i++)
		{
			int u = n + i;
			w[u] = 1;
			for(int j = 0;j < bcc[i].size();j++)
			{
				int v = bcc[i][j];
				b[u].push_back(v);
				b[v].push_back(u);	
			}
			bcc[i].clear();
		}
		for(int i = 1;i <= n;i++)
		 if(!jud[i]) dfs2(i,-1); 	
		long long Ans = 0;
		for(int i = 1;i <= n;i++)
		{
			if(Count[f[i]] == 1) Ans = (Ans + i*(TOT + MOD - w[i]) % MOD) % MOD;
			else 
			 if(iscut[i]) Ans = (Ans + i*ans[i]) % MOD;
		 	 else Ans = (Ans + i*(TOT + MOD - weight[f[i]] + weight[f[i]]*ksm(w[i],MOD-2) % MOD) % MOD ) % MOD;
		}
		cout<<Ans<<endl;
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值