洛谷传送门
题目背景
在北纬 91° ,有一个神奇的国度,叫做企鹅国。这里的企鹅也有自己发达的文明,称为企鹅文明。因为企鹅只有黑白两种颜色,所以他们的数学也是以二进制为基础发展的。
比如早在 11101001 年前,他们就有了异或这样一个数学概念。如果你不知道异或是什么,请出门过墙左转到这里。
再比如早在 1000010 年前,他们的大科学家 Penguin. Tu 就提出了图和最短路径这样一些概念。
题目描述
企鹅国中有 N N 座城市,编号从 到 N N 。
对于任意的两座城市 和 j j ,企鹅们可以花费 的时间从城市 i i 走到城市 ,这里 C C 为一个给定的常数。
当然除此之外还有 条单向的快捷通道,第 i i 条快捷通道从第 个城市通向第 Ti T i 个城市,走这条通道需要消耗 Vi V i 的时间。
现在来自 **P**enguin **K**ingdom **U**niversity 的企鹅豆豆正在考虑从城市 A A 前往城市 最少需要多少时间?
输入输出格式
输入格式:
从标准输入读入数据。
输入第一行包含三个整数 N,M,C N , M , C ,表示企鹅国城市的个数、快捷通道的个数以及题面中提到的给定的常数 C C 。
接下来的 行,每行三个正整数 Fi,Ti,Vi F i , T i , V i ( 1≤Fi≤N,1≤Ti≤N,1≤Vi≤100 1 ≤ F i ≤ N , 1 ≤ T i ≤ N , 1 ≤ V i ≤ 100 ),分别表示对应通道的起点城市标号、终点城市标号和通过这条通道需要消耗的时间。
最后一行两个正整数 A,B A , B ( 1≤C≤100 1 ≤ C ≤ 100 ),表示企鹅豆豆选择的起点城市标号和终点城市标号。
输出格式:
输出到标准输出。
输出一行一个整数,表示从城市 A A 前往城市 需要的最少时间。
输入输出样例
输入样例#1:
4 2 1
1 3 1
2 4 4
1 4
输出样例#1:
5
输入样例#2:
7 2 10
1 3 1
2 4 4
3 6
输出样例#2:
34
说明
样例1解释
直接从 1 1 走到 就好了。
样例2解释
先从 3 3 走到 ,再从 2 2 通过通道到达 ,再从 4 4 走到 。
活泼可爱的出题人给大家留下了下面这张图。
Credit: https://www.luogu.org/discuss/show/38908
解题分析
首先我们暴力连 N2 N 2 条边肯定是不科学的, 考虑如何优化。
然后我们可以发现其实两个点之间异或得到的值可以转化为若干步得到:
例如 1101 xor 0001=(1<<3)+(0101 xor 0001)=(1<<3)+(1<<2)+0 1101 x o r 0001 = ( 1 << 3 ) + ( 0101 x o r 0001 ) = ( 1 << 3 ) + ( 1 << 2 ) + 0
所以我们实际上只需要考虑每一位的转移, 即对于每个点 x x 向连一条边权为 1<<i 1 << i 的边。
所以我们就有了一个 O(Nlog2(N)) O ( N l o g 2 ( N ) ) 的算法。 博主用 pbds p b d s 卡过去了…
代码如下:
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <ext/pb_ds/priority_queue.hpp>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 100050
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
W (!isdigit(c)) c = gc;
W (isdigit(c))
x = (x << 1) + (x << 3) + c - 48, c = gc;
}
struct Pass
{int pos, dis;};
IN bool operator < (const Pass &x, const Pass &y)
{return x.dis > y.dis;}
__gnu_pbds::priority_queue <Pass> q;
int dot, line, cnt, mul, st, ed;
int head[MX], dis[MX];
struct Edge
{
int to, len, nex;
}edge[MX << 5];
IN void addedge(const int &from, const int &to, const int &len)
{edge[++cnt] = {to, len, head[from]}; head[from] = cnt;}
void dj()
{
std::memset(dis, 63, sizeof(dis));
q.push({st, 0}), dis[st] = 0;
Pass cur; R int now, i;
W (!q.empty())
{
cur = q.top(); q.pop(); now = cur.pos;
for (i = head[now]; i; i = edge[i].nex)
{
if(dis[edge[i].to] > dis[now] + edge[i].len)
{
dis[edge[i].to] = dis[now] + edge[i].len;
q.push({edge[i].to, dis[edge[i].to]});
}
}
}
}
int main(void)
{
in(dot), in(line), in(mul);
int bs = log2(dot), tar, a, b, c;
for (R int i = 0; i <= dot; ++i)//0的转移是需要考虑的, 例如1000转化到0001必须经过0的状态。
{
for (R int j = 0; j <= bs; ++j)
{
tar = i ^ (1 << j);
if(tar <= dot) addedge(i, tar, (1 << j) * mul);
}
}
for (R int i = 1; i <= line; ++i)
{
in(a), in(b), in(c);
addedge(a, b, c);
}
in(st), in(ed), dj();
printf("%d", dis[ed]);
}