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;
}