zoukankan      html  css  js  c++  java
  • P2505 [HAOI2012]道路

    传送门

    统计每条边被最短路经过几次,点数不大,考虑计算以每个点为起点时对其他边的贡献

    对于某个点 $S$ 为起点的贡献,首先跑一遍最短路,建出最短路的 $DAG$

    考虑 $DAG$ 上的某条边被以 $S$ 为起点的最短路经过的方案数,设此边为 $(u,v)$ ,那么方案数就是 $S$ 到 $u$ 的方案数,乘上 $v$ 到后面各点的方案数

    $S$ 到 $u$ 的方案数可以按拓扑序 $dp$ 一遍得到,$v$ 到后面各点的方案数可以建反图再跑一遍 $dp$

    然后就可以计算起点 $S$ 对各条边的贡献,对每个点作为起点分别计算贡献即可

    具体实现看代码,挺简单的

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<vector>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=2007,M=1e4+7,mo=1e9+7;
    inline int fk(int x) { return x>=mo ? x-mo : x; }
    int n,m;
    ll ans[M];
    int fir[N],from[M<<1],to[M<<1],val[M<<1],id[M<<1],cntt;
    inline void add(int a,int b,int c,int d)
    {
        from[++cntt]=fir[a]; fir[a]=cntt;
        to[cntt]=b; val[cntt]=c; id[cntt]=d;
    }
    int dis[N];
    struct dat {
        int x,d;
        dat (int a=0,int b=0) { x=a,d=b; }
        inline bool operator < (const dat &tmp) const {
            return d>tmp.d;
        }
    };
    priority_queue <dat> Q;
    void Dijk(int S)//求以S为起点到各个点的最短路
    {
        for(int i=1;i<=n;i++) dis[i]=mo;
        Q.push(dat(S,0)); dis[S]=0;
        while(!Q.empty())
        {
            dat x=Q.top(); Q.pop(); if(dis[x.x]!=x.d) continue;
            for(int i=fir[x.x];i;i=from[i])
            {
                int &v=to[i]; if(dis[v]<=x.d+val[i]) continue;
                dis[v]=x.d+val[i]; Q.push(dat(v,dis[v]));
            }
        }
    }
    vector <int> V[N],G[N];//V存DAG
    int du[N],f[N],g[N];//入度,S到各个点的方案,各个点到后面其他点的方案
    void Tuopu(int *F,bool type)//DAG上dp算方案数
    {
        queue <int> q;
        for(int i=1;i<=n;i++) if(!du[i]) q.push(i),F[i]=1;
        if(type) for(int i=1;i<=n;i++) F[i]=1;
        while(!q.empty())
        {
            int x=q.front(),len=V[x].size(); q.pop();
            for(int i=0;i<len;i++)
            {
                int &v=V[x][i]; F[v]=fk(F[v]+F[x]);
                du[v]--; if(!du[v]) q.push(v);
            }
        }
    }
    void calc(int S)//计算以S为起点的贡献
    {
        for(int i=1;i<=n;i++)
            du[i]=f[i]=g[i]=0,V[i].clear(),G[i].clear();
        for(int i=1;i<=n;i++)
            for(int j=fir[i];j;j=from[j])
            {
                int &v=to[j]; if(dis[v]!=dis[i]+val[j]) continue;
                V[i].push_back(v); du[v]++;
            }
        Tuopu(f,0);
        for(int i=1;i<=n;i++) G[i]=V[i],V[i].clear();
        for(int i=1;i<=n;i++)
            for(int j=G[i].size()-1;j>=0;j--) V[G[i][j]].push_back(i),du[i]++;//建反图
        Tuopu(g,1);
        for(int i=1;i<=n;i++)
            for(int j=fir[i];j;j=from[j])
            {
                int &v=to[j]; if(dis[v]!=dis[i]+val[j]) continue;
                ans[id[j]]=fk(ans[id[j]] + 1ll*f[i]*g[v]%mo );//注意long long
            }
    }
    int main()
    {
        n=read(),m=read(); int a,b,c;
        for(int i=1;i<=m;i++)
            a=read(),b=read(),c=read(),add(a,b,c,i);
        for(int i=1;i<=n;i++) Dijk(i),calc(i);
        for(int i=1;i<=m;i++) printf("%lld
    ",ans[i]);
        return 0;
    }
  • 相关阅读:
    01矩阵扩展
    蒙特卡罗仿真
    某幂相关数学结论
    分式乘法变加减
    ICPC模板排版工具
    windows下mysql使用实录
    随机题目小结
    工作用linux命令汇总
    小数化分数的O(log2n)解法
    博弈总结
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/11485829.html
Copyright © 2011-2022 走看看