题目链接: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=1n∑j=1nd(i,j)×[ai=1∧aj=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(1≤n≤105,1≤m≤2×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+⋯+2n−1<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
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;
}