CH6202 黑暗城堡 最短路径生成树

问题 G: 黑暗城堡

时间限制: 1 Sec  内存限制: 128 MB
提交: 30  解决: 11
[提交] [状态] [命题人:admin]

题目描述

在顺利攻破Lord lsp的防线之后,lqr一行人来到了Lord lsp的城堡下方。Lord lsp黑化之后虽然拥有了强大的超能力,能够用意念力制造建筑物,但是智商水平却没怎么增加。现在lqr已经搞清楚黑暗城堡有N个房间 (1≤N≤1000),M条可以制造的双向通道,以及每条通道的长度。
lqr深知Lord lsp的想法,为了避免每次都要琢磨两个房间之间的最短路径,Lord lsp一定会把城堡修建成树形的;但是,为了尽量提高自己的移动效率,Lord lsp一定会使得城堡满足下面的条件:设 D[i] 为如果所有的通道都被修建,第 i 号房间与第1号房间的最短路径长度;而 S[i] 为实际修建的树形城堡中第 i 号房间与第1号房间的路径长度;要求对于所有整数 i(1≤i≤N),有 S[i]=D[i] 成立。
为了打败Lord lsp,lqr想知道有多少种不同的城堡修建方案。于是lqr向applepi提出了这个问题。因为applepi还要忙着出模拟赛,所以这个任务就交给你了。当然,你只需要输出答案对 2^31–1 取模之后的结果就行了。

 

输入

第一行有两个整数N 和M。
之后M 行,每行三个整数X,Y 和L,表示可以修建X 和Y 之间的一条长度为L 的通道。

 

输出

一个整数,表示答案对 2^31–1 取模之后的结果。

 

样例输入

复制样例数据

3 3
1 2 2
1 3 1
2 3 1

样例输出

2

 

提示

对于30% 的数据,2≤N≤5,M≤10。
对于50% 的数据,满足条件的方案数不超过10000。
对于100% 的数据,2≤N≤1000,N – 1≤M≤N(N – 1)/2,1≤L≤100。
 

 

[提交][状态]

思路:先用dij计算出图中1到其他点的最短路径

题目要求求出一种树结构使得1到其他点的距离恰好等于最短路径,可以发现这种结构满足最短路径生成树

(最短路径生成树即树上一源点到其他点的距离都是最短路径,最小生成树则是所有路径加起来的价值最小,有区别)

即x是y的父节点,之间距离为z,那么dis[ y ] = dis [ x ] + z

既然要求方案数,自然要用乘法原理,那么就需要考虑每条边是否有贡献

设dis数组为1到其他点的最短路径

在这里,有贡献即意味着,dis [ y ] 可以由dis [ i ] 加上 点y到 i 点的距离得到,即到y点的路径可以由到 i 的路径再加上y到 i 的路径替换,那么方案数这里就加一

只有较长的距离才可以由较短的距离替换,所以求解时可以将距离从小到大排序,对每点只需考虑最短路径小于该点的点

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn = 1e3 + 100;
const int inf = 0x3f3f3f3f;
const int mod = (1 << 31) - 1;
int mapp[maxn][maxn];
int vis[maxn], dis[maxn];
int n, m;
struct node {
    int id, d;
} s[maxn];

bool cmp(node a, node b) {
    return a.d < b.d;
}

void dij() {
    memset(vis, 0, sizeof(vis));
    memset(dis, 0x3f, sizeof(dis));
    dis[1] = 0;
    for (int i = 1; i < n; i++) {
        int p = 0;
        for (int j = 1; j <= n; j++) {
            if (!vis[j] && (p == 0 || dis[j] < dis[p])) p = j;
        }
        vis[p] = 1;
        for (int j = 1; j <= n; j++) {
            dis[j] = min(dis[j], dis[p] + mapp[p][j]);
        }
    }
}

int main() {

    scanf("%d%d", &n, &m);
    memset(mapp, 0x3f, sizeof(mapp));
    int u, v, c;
    for (int i = 1; i <= m; i++) {
        scanf("%d%d%d", &u, &v, &c);
        mapp[u][v] = mapp[v][u] = min(mapp[u][v], c);
    }
    dij();
    for (int i = 1; i <= n; i++) {
        s[i].id = i;
        s[i].d = dis[i];
    }
    sort(s + 1, s + n + 1, cmp);
    ll ans = 1;
    for (int i = 2; i <= n; i++) {
        ll cnt = 0;
        for (int j = 1; j < i; j++) {
            int y = s[i].id, x = s[j].id;
            if (dis[y] == dis[x] + mapp[x][y])
                cnt++;
        }
        ans = ans * cnt % mod;
    }
    printf("%lld\n", ans);
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值