zoukankan      html  css  js  c++  java
  • 2020hdu多校第六场1006A Very Easy Graph Problem

    题目地址http://acm.hdu.edu.cn/showproblem.php?pid=6832

    题意:在一个n个结点,m条边的无向连通图中,且第i条边的权值为2i,每个结点有一个值,为1或者0。d(i,j)表示结点i到结点j之间的最短距离。对所有节点求所有的可能配对形式d(i,j)*[a[i]==1^a[0]==0]的和,最后对1e9+7求余。

    输入:第一行t,表示样例个数。每个样例第一行两个整数n,m。接下来一行n个整数,表示结点的权值,接下来m行,每行u,v表示u,v之间存在一条边

    输出:每个样例输出一个整数

    题解:在这里我们需要注意这个权值是比较特殊的,也就是如果第i条边(权值为2i)是连接结点j和结点k,但是在这之前j和k已经相连了,那么这条边(权值2i)永远都不会被最短路经过,因为前i-1条边的总权值是2i-2,小于2i.所以我们便可以将这个无向连通图转换为一棵最小生成树(既是最小生成树也是任意两个结点之间的距离都是最小)。这里的n,m都是1e5的数量级别,所以如果暴力求两个结点的最短路那么肯定会超时。所以我们可以转换一下思路,求每条边的贡献值,也就是如果这条边最后会出现在最短路中,我们需要计算的是在最短路中出现了多少次,可以求出这条边两边各自的1和0的数量,那么这条边在最短路中的次数就是left_0*right_1+left_1*right_0,那么这条边的在最后的结果中的贡献就是(left_0*right_1+left_1*right_0)*这条边的权值。那么现在主要的就是求出每条边两边的1和0的数量,可以使用dfs进行求解。

    AC代码

    #include<iostream>
    #include<vector>
    using namespace std;
    #define ll long long  int
    const ll N=2e5+5;
    const ll mod=1e9+7;
    vector<pair<int,ll> >E[N];
    int fa[N],a[N],sum0,sum1,k;
    struct edge{
        int p0,p1;
        ll w;
    }b[N];
    struct edge operator+(struct edge b1,struct edge b2){//重载运算符 
        struct edge b3;
        b3.p0=b1.p0+b2.p0;
        b3.p1=b1.p1+b2.p1;
        b3.w=0;
        return b3;
    }
    int find(int x){
        if(x==fa[x]) return x;
        else return fa[x]=find(fa[x]);
    }
    void merge(int x,int y){
        int rx=find(x),ry=find(y);
        fa[rx]=ry;
    }
    struct edge dfs(int now,int pre){
        int size=E[now].size();
        pair<int,ll>pa;
        int tem=k;//使用数组存储遍历后边的信息 
        k++;
        ll w=0;
        for(int i=0;i<size;i++){
            pa=E[now][i];
            if(pre==pa.first){//当与上一次遍历的节点相同时,先存储这条边的权重 
                w=pa.second;
                continue;
            }
            b[tem]=b[tem]+dfs(pa.first,now);
        }
        b[tem].w=w;//存储的是now与pre两个结点之间的边的信息 
        if(a[now]==0) b[tem].p0++;
        else b[tem].p1++;
        return b[tem];
    }
    int main(){
        int t;cin>>t;
        int n,m;
        while(t--){
            cin>>n>>m;sum0=0,sum1=0;a[0]=0;k=0;
            for(int i=1;i<=n;i++) E[i].clear(); //这里一定要有,之前没有,一直runtime error 
            for(int i=1;i<=n;i++) fa[i]=i;//并查集初始化。并查集用于求解最小生成树 
            for(int i=0;i<=n;i++) b[i].p0=0,b[i].p1=0,b[i].w=0; //初始化 
            for(int i=1;i<=n;i++){ //输入结点权值 
                cin>>a[i];
                if(a[i]==1) sum1++; //记录所有的1和0的数量。到时就可以只计算边的一边的1和0的数量就可以了 
                else sum0++;
            }
            int u,v;
            ll base=1;//边的权值 
            for(int i=1;i<=m;i++){
                cin>>u>>v;
                base*=2;
                base%=mod;
                if(find(u)==find(v)) continue;
                merge(u,v);
                E[v].push_back(make_pair(u,base));//使用邻接表存储树的信息 
                E[u].push_back(make_pair(v,base));
            }
            dfs(1,-1);//dfs求解树中每条边的贡献次数 
            ll sum=0;
            for(int i=1;i<n;i++){//求解最后的结果 
                sum=sum+(sum0-b[i].p0)*b[i].p1*b[i].w;
                sum%=mod;
                sum=sum+(sum1-b[i].p1)*b[i].p0*b[i].w;
                sum%=mod;
            }
            cout<<sum<<endl;
        }
        return 0;
    }

    写于2020/8/7 12:16


    作者:孙建钊
    出处:http://www.cnblogs.com/sunjianzhao/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    Java中使用Jedis连接Redis对Hash进行操作的常用命令
    Java中使用Jedis连接Redis对Set进行操作的常用命令
    Java中使用Jedis连接Redis对List进行操作的常用命令
    Android中获取定位经纬度信息
    Java中使用Jedis连接Redis对String进行操作的常用命令
    Java中使用Jedis连接Redis对Key进行操作的常用命令
    Java中使用Jedis连接池连接Redis数据库流程
    Java中使用Jedis连接Redis数据库流程
    Java中使用Jedis连接Redis服务端时提示:JedisConnectionException: Failed connecting
    调用百度翻译对句子进行翻译
  • 原文地址:https://www.cnblogs.com/sunjianzhao/p/13452030.html
Copyright © 2011-2022 走看看