2020杭电HDU-6832多校第六场A Very Easy Graph Problem(最短路转最小生成树+dfs)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6832
博客园食用链接:https://www.cnblogs.com/lonely-wind-/p/13455277.html

An undirected connected graph has n nodes and m edges, The i-th edge’s length is 2 i 2^i 2i. Each node i has a value a i a_i ai, which is either 0 or 1. You need to calculate:
∑ i = 1 n ∑ j = 1 n d ( i , j ) × [ a i = 1 ∧ a j = 0 ] \sum_{i=1}^n\sum_{j=1}^nd(i,j)\times [a_i=1∧a_j=0] i=1nj=1nd(i,j)×[ai=1aj=0]
d ( i , j ) d(i,j) d(i,j)ndicates the shortest distance between i and j. [ ] is the Iverson bracket. ∧ indicates AND.

Because the answer may be too large, please output the answer modulo 1 0 9 + 7 10^9+7 109+7.

Input
The first line contains one integer T(1≤T≤8),indicating the number of test cases.

The second line contains two ingeters n , m ( 1 ≤ n ≤ 1 0 5 , 1 ≤ m ≤ 2 × 1 0 5 ) n,m(1≤n≤10^5,1≤m≤2×10^5) n,m(1n105,1m2×105).

The third line contains n positive integers a 1 , a 2 , . . . , a n ( a i = 0   o r   1 ) a_1,a_2,...,a_n(a_i=0\ or\ 1) a1,a2,...,an(ai=0 or 1) —— the value of the nodes.

The following m lines contain two ingeters u,v(1≤u,v≤n), and the i-th line represents the i-th undirected edge’s length is 2 i 2^i 2i, between node u and v.

The sum of n,m is no more than 2 × 1 0 5 2×10^5 2×105.
Output
Print a single integer—— the value of the answer modulo 1 0 9 + 7 10^9+7 109+7.

Sample Input
1
3 2
0 1 0
3 1
3 2
Sample Output
10

题目大意:给你一个图,n个点每个点的点有0,1两种颜色,m条边,第 i i i条边的长度位 2 i 2^i 2i,现在问你每个1点到每个0点的最小距离之和。

emmm,神仙队友什么都会。。。。我全程划水QAQ
实际上给了你这么多的边,有很多一部分是没有用的,比如说,对于第 i i i条边而言,如果它所连接的 u , v u,v u,v两点在第 i i i条边之前就已经被连接了,那么根据 2 1 + 2 2 + ⋯ + 2 n − 1 < 2 n 2^1+2^2+\cdots+2^{n-1}<2^n 21+22++2n1<2n,这条边就是可以直接扔掉的了,那么根据这个关系我们似乎可以联想到kruskal生成树,它也是按照每个边的权值排序后进行建边的,那么我们就可以构造一个生成树了!

那么现在建好树了,我们需要计算书上每个黑点到每个白点的距离之和,这个方法似乎挺多的,不过蒟蒻的我只能写个超级麻烦的dfs来处理,我们可以计算一下每条边的左右两侧各有多少黑点和白点,实际上也就是子树和非子树中的黑白点的分布。具体这么算呢?可以参考如下代码段:(写的有点麻烦和丑陋QAQ)

struct Col
{
	int nbw,nbb;
	Col operator+(const Col &a)const{
		return Col{nbw+a.nbw,nbb+a.nbb};
	}
};

Col dfs(int x,int fa)
{
	Col cl=Col{0,0};
	for (int i=head_tree[x]; i!=-1; i=eg_tree[i].next){
		int v=eg_tree[i].to;
		if (v==fa) continue;
		Col p=dfs(v,x);
		cl=cl+p;
		nb_white[eg_tree[i].w]=p.nbw;//该边以下的白点数
		nb_black[eg_tree[i].w]=p.nbb;
	}
	if (color[x]==0) return cl+Col{1,0};
	return cl+Col{0,1};
}

那么现在就似乎结束了,我们最后在枚举树中的边,然后将黑点白点配一下对就好了。
以下是AC代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int mac=2e5+10;
const int mod=1e9+7;

struct node
{
	int up,to,w;
}eg[mac<<1];
struct Tree
{
	int to,next,w;
}eg_tree[mac];
int num_tree=0,head_tree[mac],color[mac];
int father[mac],nb_black[mac],nb_white[mac];
vector<int>edge;

void add(int u,int v,int w)
{
	eg_tree[num_tree]=Tree{v,head_tree[u],w};
	head_tree[u]=num_tree++;
}

void init(int n)
{
	num_tree=0;  edge.clear();
	memset(head_tree,-1,sizeof head_tree);
	for (int i=1; i<=n; i++) father[i]=i;
}

int find(int x){return x==father[x]?x:father[x]=find(father[x]);}

void kruskal(int m,int n)
{
	int nb=0;
	for (int i=0; i<=m; i++){
		int fu=find(eg[i].up);
		int fv=find(eg[i].to);
		if (fu==fv) continue;
		father[fu]=fv;
		nb++;
		add(eg[i].up,eg[i].to,eg[i].w); 
		add(eg[i].to,eg[i].up,eg[i].w);
		edge.push_back(eg[i].w);
		if (nb==n-1) break;
	}
}

ll qpow(ll a,ll b)
{
	ll ans=1;
	while (b){
		if (b&1) ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}

struct Col
{
	int nbw,nbb;
	Col operator+(const Col &a)const{
		return Col{nbw+a.nbw,nbb+a.nbb};
	}
};

Col dfs(int x,int fa)
{
	Col cl=Col{0,0};
	for (int i=head_tree[x]; i!=-1; i=eg_tree[i].next){
		int v=eg_tree[i].to;
		if (v==fa) continue;
		Col p=dfs(v,x);
		cl=cl+p;
		nb_white[eg_tree[i].w]=p.nbw;
		nb_black[eg_tree[i].w]=p.nbb;
	}
	if (color[x]==0) return cl+Col{1,0};
	return cl+Col{0,1};
}

int main(int argc, char const *argv[])
{
	int t,n,m;
	scanf ("%d",&t);
	while (t--){
		scanf ("%d%d",&n,&m);
		init(n);
		int white=0,black=0;
		for (int i=1; i<=n; i++){
			scanf ("%d",&color[i]);
			if (color[i]==0) white++;
			else black++;
		}
		for (int i=1; i<=m; i++){
			int u,v;
			scanf ("%d%d",&u,&v);
			eg[i-1]=node{u,v,i};
		}
		kruskal(m-1,n);
		dfs(1,-1);
		ll ans=0;
		for (auto x:edge){
			int wt=white-nb_white[x];
			int bk=black-nb_black[x];
			ans=(ans+(wt*nb_black[x]%mod)*qpow(2,x)%mod)%mod;
			ans=(ans+(bk*nb_white[x]%mod)*qpow(2,x)%mod)%mod;
		}
		printf("%lld\n",ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值