• HDU6821 A Very Easy Graph Problem(最小生成树)


    题意:

    给出一个无向连通图,里面的点分为0号点和1号点,第i条边的边权是2的i次。

    询问所有1号点到0号点的最短路径之和。

    题解:

    如果对所有1号点跑dijkstra算法,时间肯定是无法接受的。

    观察到题目的边权有一个关键的性质,第i条边权是2的i次,这说明前i-1条边加起来都没这条边的边权大。

    猜想,所有最短路径都在这个图的最小生成树上。

    所以先建立最小生成树,树上的所有边对答案都是有贡献的,现在考虑单边对答案的贡献。我们可以用一遍DFS处理出每条边左边的1号点个数、右边的0号点个数,两者相乘再乘上这条边的权值,左右相反同理,这样就可以算出单边对答案的贡献。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=2e5+100;
    const int mod=1e9+7;
    const ll inf=1e18;
    typedef long long ll;
    struct node {
        int u,v;
        ll w;
        int nxt;
    }edge[maxn];
    int head[maxn];
    int tot;
    void addedge (int u,int v,int w) {
        edge[tot].u=u;
        edge[tot].v=v;
        edge[tot].w=w;
        edge[tot].nxt=head[u];
        head[u]=tot++;
        
        edge[tot].u=v;
        edge[tot].v=u;
        edge[tot].w=w;
        edge[tot].nxt=head[v];
        head[v]=tot++;
    } 
    ll w[maxn];
    int a[maxn];//节点权值 
    int father[maxn];
    int findfather (int x) {
        int a=x;
        while (x!=father[x]) x=father[x];
        while (a!=father[a]) {
            int z=a;
            a=father[a];
            father[z]=x;
        }
        return x;
    }
    ll lst[maxn];//每个节点和他父亲连的那条边
    int dfn[2][maxn];
    int dfo[2][maxn];
    
    int cnt[2];
    int wjm[2][maxn];
    void dfs (int u,int pre) {
        cnt[a[u]]++;
        dfn[0][u]=cnt[0];
        dfn[1][u]=cnt[1];
        for (int i=head[u];i!=-1;i=edge[i].nxt) {
            int v=edge[i].v;
            if (v==pre) continue;
            lst[v]=edge[i].w;
            dfs(v,u);
        }
        dfo[0][u]=cnt[0];
        dfo[1][u]=cnt[1];
    } 
    
    int main () {
        w[0]=1;
        for (int i=1;i<maxn;i++) w[i]=(w[i-1]*2)%mod;
        int t,n,m;
        scanf("%d",&t);
        while (t--) {
            scanf("%d%d",&n,&m);
            for (int i=0;i<=n;i++) head[i]=-1;
            tot=0;
            int sum[2];
            memset(sum,0,sizeof(sum));
            cnt[0]=0;
            cnt[1]=0;
            for (int i=1;i<=n;i++) scanf("%d",a+i),father[i]=i,sum[a[i]]++,wjm[0][i]=0,wjm[1][i]=0,lst[i]=0;
            for (int i=1;i<=m;i++) {
                int u,v;
                scanf("%d%d",&u,&v);
                int faU=findfather(u);
                int faV=findfather(v);
                if (faU==faV) continue;
                father[faU]=faV;
                addedge(u,v,w[i]);
            }
            ll ans=0;
            dfs(1,0);
            for (int i=1;i<=n;i++) {
                //每个点与他父亲连接的边的贡献
                wjm[0][i]=dfo[0][i]-dfn[0][i];
                wjm[1][i]=dfo[1][i]-dfn[1][i];
                wjm[a[i]][i]++;
                ll x=wjm[0][i];//子树里0号点
                ll y=sum[1]-wjm[1][i];//除子树外的1号点
                ll x1=wjm[1][i];//子树里1号点
                ll y1=sum[0]-wjm[0][i];//除子树外0号点
                
                ans=ans+(lst[i]*x%mod)*y%mod+(lst[i]*x1%mod)*y1%mod;
                ans%=mod;
            }
        
            printf("%lld
    ",ans);
        }
    }
  • 相关阅读:
    Oracle Sql优化之报表和数据仓库运算
    Oracle Sql优化之Rownum的使用
    Oracle Sql优化之范围处理
    c++11编译指令
    python 爬小说
    exvim快捷键
    几个崩溃问题
    网络穿透
    c# 拉取网页
    tars安装记
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/13448187.html
Copyright © 2020-2023  润新知