CH6202·黑暗城堡

初见安~这里是一个我没用过的网站的和bzoj的入门OJ的传送门:CH6202 & 入门OJ P4928

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;
}

有点暴力呀……但是还是可以180+ms完美通过的~

超级开心,因为是自己写出来然后一遍过的>w<

迎评:)
——End——

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值