AcWing 黑暗城堡
Description
在顺利攻破Lord lsp的防线之后,lqr一行人来到了Lord lsp的城堡下方。
Lord lsp黑化之后虽然拥有了强大的超能力,能够用意念力制造建筑物,但是智商水平却没怎么增加。
现在lqr已经搞清楚黑暗城堡有N个房间,M条可以制造的双向通道,以及每条通道的长度。
lqr深知Lord lsp的想法,为了避免每次都要琢磨两个房间之间的最短路径,Lord lsp一定会把城堡修建成树形的。
但是,为了尽量提高自己的移动效率,Lord lsp一定会使得城堡满足下面的条件:
设 D[i] 为如果所有的通道都被修建,第 i 号房间与第1号房间的最短路径长度;而 S[i] 为实际修建的树形城堡中第 i 号房间与第1号房间的路径长度;要求对于所有整数 i,有 S[i]=D[i] 成立。
为了打败Lord lsp,lqr想知道有多少种不同的城堡修建方案。
你需要输出答案对 2 ^ 31–1
取模之后的结果。
Input
第一行有两个整数 N 和 M。
之后 M 行,每行三个整数X,Y 和L,表示可以修建 X 和 Y 之间的一条长度为 L 的通道。
Output
- 一个整数,表示答案对 2 ^ 31–1 取模之后的结果。
Sample Input
3 3 1 2 2 1 3 1 2 3 1
Sample Output
2
题解:
题解来自mzx
很显然题目要求的是一个图的生成树,这棵生成树要求满足根节点到每个结点的距离都等于原图中的最短距离
我们不妨将这样的树称之为这个图的最短路径生成树
首先生成树中的边肯定是最短路径子图中的边
我来解释一下这个概念……
首先图是由点集和边集两个元素组成的
那么子图的点集和边集肯定是原图中的子集
最短路径子图的点集肯定和原图一样
顾名思义边集就是所有可能出现在最短路径中的边的集合
即E'={e|dis[e.u]+e.l=dis[e.v]}
我们可以用显然法证明,最短路径生成树一定是最短路径子图的生成树
然鹅最短路径子图的生成树却并不一定是最短路径生成树_(:зゝ∠)_
举个例子
V={1,2,3},E={(1,2,1),(2,3,1),(1,3,2)}(最后一个元素代表边的长度)
显然这个图的每一条边都可能出现在最短路径中
所以这个图的最短路径子图就是它本身
{(1,3,2)(2,3,1)}是这个图的最短路径子图的一个生成树
然鹅并不是这个图的最短路径生成树,因为1到2的距离是3
有了以上的思路之后,我们可以先求出来最短路径子图
最短路径子图怎么求?
根据定义,跑一遍任何一个单源最短路算法(dij,SPFA),然后dis[i]+l[i][j]=dis[j]的边就是最短路径子图的边
然后我们将这些点按dis排序
假设我们现在已经求出了连通1...i-1这几个点的最短路径生成树个数
首先第i个点肯定是由1...i-1中的其中一个点连接的,而不可能是dis比i还大的那些点连接过来的。
然后1...i-1的生成树的形态和1...i-1中哪个点连接i这个点是没有关系的
也就是说这两个方案是相互独立的,计数中的互相独立意味着乘法原理的可行性
那么连通1...i的最短路径生成树个数就是1...i-1的生成树个数乘以1...i-1中任何一个点连接i这个点的最短路边的个数
讲完了
是不是很玄学……我也觉得,很想细讲但是发现没办法再讲了,自己脑补吧……
- 我补充下自己的理解吧,根据题目要求,若x是y的父亲,x、y之间的道路长度为z。则有dis[y] = dis[x] + z。
- 那么我们可以对于每个点,枚举它的出边,如果有上述的关系。说明可以从出边连的那个点向当前点连一条边(有可能可以有多个点与当前点有上述关系)。即一层一层的建树,每层有a种方法,建b层。是满足乘法原理的。
#include
#include
#include
#include
#define N 1005
#define M 500005 * 2
#define int long long
#define hry ((1 << 31) - 1)
using namespace std;
struct Node
{
int val, pos;
friend bool operator < (Node x, Node y) {
return x.val > y.val;
}
};
struct E {int next, to, dis;} e[M];
int n, m, num, ans = 1;
int h[N], dis[N], cnt[N];
bool vis[N];
int read()
{
int x = 0; char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();}
return x;
}
void add(int u, int v, int w)
{
e[++num].next = h[u];
e[num].to = v;
e[num].dis = w;
h[u] = num;
}
void dijkstra()
{
memset(dis, 0x3f, sizeof(dis));
priority_queue que;
que.push((Node){0, 1}), dis[1] = 0;
while(que.size())
{
int now = que.top().pos;
que.pop();
if(vis[now]) continue;
vis[now] = 1;
for(int i = h[now]; i != 0; i = e[i].next)
if(dis[now] + e[i].dis < dis[e[i].to])
{
dis[e[i].to] = dis[now] + e[i].dis;
que.push((Node){dis[e[i].to], e[i].to});
}
}
}
signed main()
{
cin >> n >> m;
for(int i = 1; i <= m; i++)
{
int u = read(), v = read(), w = read();
add(u, v, w), add(v, u, w);
}
dijkstra();
cnt[1] = 1;
for(int i = 1; i <= n; i++)
for(int j = h[i]; j != 0; j = e[j].next)
if(dis[i] + e[j].dis == dis[e[j].to])
cnt[e[j].to]++;
for(int i = 1; i <= n; i++)
ans = ((cnt[i] % hry) * ans) % hry;
cout << ans;
return 0;
}