zoukankan      html  css  js  c++  java
  • 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}^nsum_{j=1}^nd(i,j) imes [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
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    设计模式研究
    requests模块请求常用参数的写法整理
    python程序打包exe文件
    爬虫响应信息乱码解决方式
    Vue-cli父子组件之间传参
    MYSQL事件隔离级别以及复读,幻读,脏读的理解
    [NOIP2009] 提高组 洛谷P1073 最优贸易
    [NOIP2009] 提高组 洛谷P1071 潜伏者
    [NOIP2009] 普及组
    洛谷P3386 【模板】二分图匹配
  • 原文地址:https://www.cnblogs.com/lonely-wind-/p/13455277.html
Copyright © 2011-2022 走看看