这个题其实没有想象中那么难的……中等难度就差不多了。
Description
在顺利攻破 Lord lsp 的防线之后,lqr 一行人来到了 Lord lsp 的城堡下方。Lord lsp 黑化之后虽然拥有了强
大的超能力,能够用意念力制造建筑物,但是智商水平却没怎么增加。现在 lqr 已经搞清楚黑暗城堡有 N 个房间
,M 条可以制造的双向通道,以及每条通道的长度。lqr 深知 Lord lsp 的想法,为了避免每次都要琢磨两个房间
之间的最短路径, Lord lsp一定会把城堡修建成树形的;但是,为了尽量提高自己的移动效率,Lord lsp 一定会
使得城堡满足下面的条件:设 Di为如果所有的通道都被修建,第 i 号房间与第 1 号房间的最短路径长度;而 Si
为实际修建的树形城堡中第 i 号房间与第1 号房间的路径长度,对于所有满足 1≤i≤N 的整数 i,有 Si = Di
。为了打败 Lord lsp,lqr想知道有多少种不同的城堡修建方案。于是 lqr 向 applepi 提出了这个问题。由于 a
pplepi 还要忙着出模拟赛,所以这个任务就交给你了。当然,你只需要输出答案对 2^31 – 1 取模之后的结果就
行了.
Input
第一行有两个整数 N 和 M。
之后 M 行,每行三个整数 X,Y 和 L,表示可以修建 X 和 Y 之间的一条长度为 L 的通道。
2≤N≤1000,N – 1≤M≤N(N – 1)/2,1≤L≤100
Output
输出一个整数,表示答案对 2^31 – 1 取模之后的结果。
Sample Input
3 3
1 2 2
1 3 1
2 3 1
Sample Output
2
Sol
这是一个最短路径树 的典例之一。题意很明确——问你有多少棵最短路径树。
虽然看起来n比较大,但是是单源的所以不用担心复杂度会爆掉。求一遍最短路,然后枚举每个点以及连出去的边,看看有多少条到达这个点的路径长度是等于最短路径的。最后累乘起来就行了。
可以直接看代码的:)
#include<bits/stdc++.h>
#define maxn 1005
#define maxm 500005
#define INF 2147483647
using namespace std;
int read()
{
int x = 0, ch = getchar();
while(!isdigit(ch)) ch = getchar();
while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
return x;
}
int n, m;
long long ans = 1;
struct edge
{
int to, w, nxt;
edge() {}
edge(int tt, int ww, int nn) {to = tt, w = ww, nxt = nn;}
}e[maxm << 1];
int head[maxn], k = 0;
void add(int u, int v, int w)
{
e[k] = edge(v, w, head[u]);
head[u] = k++;
}
int dis[maxn];
bool vis[maxn];
void dij()//Dijkstra堆优化求最短路
{
memset(dis, 0x3f, sizeof dis);
priority_queue<pair<int, int > > q;
q.push(make_pair(0, 1)); dis[1] = 0;
while(q.size())
{
register int u = q.top().second, v, w; q.pop();
if(vis[u]) continue;
vis[u] = 1;
for(int i = head[u]; ~i; i = e[i].nxt)
{
v = e[i].to, w = e[i].w;
if(dis[u] + w >= dis[v]) continue;
dis[v] = dis[u] + w;
q.push(make_pair(-dis[v], v));
}
}
}
int cnt[maxn];
int main()
{
memset(head, -1, sizeof head);
n = read(), m = read();
register int u, v, w;
for(int i = 1; i <= m; i++)
u = read(), v = read(), w = read(), add(u, v, w), add(v, u, w);
dij();
for(int i = 1; i <= n; i++)
{
for(int j = head[i]; ~j; j = e[j].nxt)//枚举点和扩散出去的边
{
v = e[j].to, w = e[j].w;
if(dis[v] == dis[i] + w) cnt[v]++;//注意cnt一定要开一个数组,因为是用i去更新v
}
}
for(int i = 1; i <= n; i++)
if(cnt[i]) ans = ans * cnt[i] % INF;//注意要取模
printf("%lld\n", ans);
return 0;
}
有点暴力呀……但是还是可以完美通过的~
超级开心,因为是自己写出来然后一遍过的>w<
迎评:)
——End——
这样的计数方式。为什么不会重复 或者遗漏
所以有大佬可以告诉我这题与最小生成树有什么关系吗?谢谢您了。
其实没有什么关系 ~ 个人感觉和 DP 关系还大一点
YingLi大佬,请问暴力枚举怎么做??
太强大了吧!!
YingLi大佬 为啥不能从其他点跑最短路 然后判断有多少种方案 一定要从第一个点跑 我发现从其他点跑会wa
题目不是就说的从第一个点跑吗【瑟瑟发抖
哦 好
### YingLi妹子太强了.
居然有妹子._.
世界上真的存在程序媛这种东西吗