1. 程式人生 > 實用技巧 >2020杭電HDU-6832多校第六場A Very Easy Graph Problem(最短路轉最小生成樹+dfs)

2020杭電HDU-6832多校第六場A Very Easy Graph Problem(最短路轉最小生成樹+dfs)

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=6832
CSDN園食用連結:https://blog.csdn.net/qq_43906000/article/details/107870347

An undirected connected graph has n nodes and m edges, The i-th edge’s length is \(2^i\). Each node i has a value \(a_i\), which is either 0 or 1. You need to calculate:
\(\sum_{i=1}^n\sum_{j=1}^nd(i,j)\times [a_i=1∧a_j=0]\)


\(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 \(10^9+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≤10^5,1≤m≤2×10^5)\)

.

The third line contains n positive integers \(a_1,a_2,...,a_n(a_i=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\), between node u and v.

The sum of n,m is no more than \(2×10^5\)

.
Output
Print a single integer—— the value of the answer modulo \(10^9+7\).

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

題目大意:給你一個圖,n個點每個點的點有0,1兩種顏色,m條邊,第\(i\)條邊的長度位\(2^i\),現在問你每個1點到每個0點的最小距離之和。

emmm,神仙隊友什麼都會。。。。我全程划水QAQ
實際上給了你這麼多的邊,有很多一部分是沒有用的,比如說,對於第\(i\)條邊而言,如果它所連線的\(u,v\)兩點在第\(i\)條邊之前就已經被連線了,那麼根據\(2^1+2^2+\cdots+2^{n-1}<2^n\),這條邊就是可以直接扔掉的了,那麼根據這個關係我們似乎可以聯想到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;
}