zoj3408-Gao-题解

Brief Description:

给出一个有向图,节点编号0->n-1,现在求0到其他点的最短路,考虑所有最短路的路径,求某节点出现多少次。

Analysis:

此题还是很容易yy的。

首先求0到其他点的最短路,然后根据得到的最短路的值求出一个DAG,保证DAG里所有从0开始的路径都是最短路.

设dp1[u]是从u开始到其他点的最短路的个数,dp2[u]是其他点到u的最短路的个数。

求u出现多少次就是 dp1[u]*dp2[u]

求dp1[u],dp2[u]可以用DAG上的dp解决,为了方便处理引入一个终止节点,所有点都连向这个终止节点,至于为什么,自己脑补:)

宏观上这么做算法是对的,但是此题trick颇多。

1.答案要求保留后10位,所以要%(10^10),但是两个10位数相乘会超longlong,所以得用布斯乘法,具体见代码

2.图可以有自环,可以有重边,因为这个错了无数次!!!


代码如下:

// ZOJ3408
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <stack>

using namespace std;
typedef long long LL;
const LL mod = 10000000000LL;
const int maxn = 10006;
struct node
{
    int id;
    int w;
    node() {}
    node(int _id,int _w)
    {
        id = _id;
        w = _w;
    }
    bool operator<(const node &p)const
    {
        return w > p.w; // basic coding
    }
};

int n,m,q;
int dis[maxn];
vector<node>V[maxn];
bool vis[maxn];
priority_queue<node>pq; //йтве╥ехКdijжп

void dij()
{
    int i;
    int Size = (int)V[1].size();
    while(!pq.empty()) pq.pop(); // basic coding

    memset(dis,0x3f,sizeof(dis));
    dis[1] = 0;
    memset(vis,0,sizeof(vis));
    vis[1] = 1;
    for(i=0; i<Size; i++)
    {
        pq.push(node(V[1][i].id,V[1][i].w));
        if(V[1][i].id != 1 && V[1][i].w < dis[V[1][i].id])
           dis[V[1][i].id] = V[1][i].w;
    }

    node T;
    while(!pq.empty())  // basic doding..
    {
        T = pq.top();
        pq.pop();
        if(!vis[T.id])
        {
            vis[T.id] = 1;
            Size = (int)V[T.id].size();
            for(i=0; i<Size; i++)
            {
                int id = V[T.id][i].id;
                int w = V[T.id][i].w;
                if(!vis[id] && dis[T.id]+w<dis[id])
                {
                    dis[id] = dis[T.id] + w;
                    pq.push(node(id,dis[id]));
                }
            }
        }
    }
}

vector<node>G[2][maxn];
LL dp1[maxn],dp2[maxn];
void getG()
{
    for(int u=1; u<=n; u++)  // get every edge
    {
        int S = (int)V[u].size();
        for(int i=0; i<S; i++)
        {
            int v = V[u][i].id;
            int w = V[u][i].w;
            if(dis[u]+w==dis[v]) // Good!
            {
                G[0][u].push_back(node(v,w));
                G[1][v].push_back(node(u,w));
            }
        }
    }
    for(int i=1; i<=n; i++)
    {
        G[1][n+1].push_back(node(i,0));
        G[0][i].push_back(node(n+1,0));
    }
}

LL DFS(int u,LL *dp,int pos)
{
    if(vis[u]) return dp[u];
    int S = (int)G[pos][u].size();
    if(S == 0)
    {
        dp[u] = 1;
        vis[u] = 1;
        return 1;
    }

    for(int i=0; i<S; i++)
    {
        int v = G[pos][u][i].id;
        LL tmp = DFS(v,dp,pos);
        dp[u] = ( dp[u] + tmp  ) % mod;
    }
    vis[u] = 1;
    return dp[u];
}

LL Mul(LL lhs,LL rhs) // basic coding
{
    LL lhs2=lhs%100000;
    LL rhs2=rhs%100000;
    return ((lhs/100000*rhs2+rhs/100000*lhs2)*100000+lhs2*rhs2)%mod;
}

int main()
{
    while(scanf("%d%d%d",&n,&m,&q) == 3)
    {
        for(int i=0; i<=n+1; i++)
        {
            G[0][i].clear();
            V[i].clear();
            G[1][i].clear();
            dp1[i] = dp2[i] = vis[i] = 0;
        }
        // G[1][n+1].clear();
        for(int i=1; i<=m; i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            u++;
            v++;
            V[u].push_back(node(v,w));
        }
        dij();
        getG();
        memset(vis,0,sizeof(vis));
        DFS(1,dp1,0);
        memset(vis,0,sizeof(vis));
        DFS(n+1,dp2,1);
        LL an;
        while(q--)
        {
            int u;
            scanf("%d",&u);
            u++;
            if(u > n)
            {
                printf("%010lld\n",0);
                continue;
            }
            an = Mul(dp1[u],dp2[u]);
            printf("%010lld\n",an);
        }
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值