传送门:牛客
题目描述:
企鹅国中有
N
N
N 座城市,编号从
1
1
1 到
N
N
N 。
对于任意的两座城市
i
i
i 和
j
j
j ,企鹅们可以花费
(
i
x
o
r
j
)
×
C
(i~\mathrm{xor}~j) \times C
(i xor j)×C 的时间从城市
i
i
i 走到城市
j
j
j ,这里
C
C
C 为一个给定的常数。
当然除此之外还有
M
M
M 条单向的快捷通道,第
i
i
i 条快捷通道从第
F
i
F_i
Fi 个城市通向第
T
i
T_i
Ti 个城市,走这条通道需要消耗
V
i
V_i
Vi 的时间。
现在来自 Penguin Kingdom University 的企鹅豆豆正在考虑从城市
A
A
A 前往城市
B
B
B 最少需要多少时间?
输入:
7 2 10
1 3 1
2 4 4
3 6
输出:
34
PKU当年CodePlus 第 4 次月赛,思维难度较大,解法也十分巧妙
主要思路:
- 首先观察题目,显然我们会发现这是一道求最短路的题目,对于我们的快捷通道来说,没有任何问题,直接添边即可,难点在于两点直接的花费
- 对于两点直接的花费,我们肯定不能直接添边,因为 n n n的范围达到了 1 e 5 1e5 1e5,如果我们暴力添边的话需要 n 2 n^2 n2的时间以及 n 2 n^2 n2的空间,显然是难以接受的,所以我们需要优化我们的添边方法
- 对于我们的添边,我们发现我们两点直接的距离其实是通过异或来决定的,所以我们考虑一下异或的性质,我们会发现其实异或的花费也遵守我们的累加规律!!就比如我们的 A , B , C A,B,C A,B,C三个点,假如我们此时的 A A A和 B B B的二进制位差一位,然后 B B B和 C C C的二进制也差一位,并且差的位置不同的话,那么我们会发现此时我们的 A A A与 C C C差的位数和位置刚好是前面的和!!举一个栗子:假如我们需要计算 1 − > 6 1->6 1−>6的距离,那么我们发现1的二进制位为(001),然后我们的6位(110),那么我们先算1到5的距离,也就是花费了 4 C 4C 4C,然后从5到7,花费了 2 C 2C 2C,然后从7到6花费了 1 C 1C 1C,此时一共花费了 7 C 7C 7C,那么其实我们直接从1->6的花费其实也是7C,所以我们发现只要我们计算出了每一个点到每一次只改变一位的另一个点的位置,那么我们就可以算出两点之间的距离了.这样的话,我们就压缩了我们的边数
for(int i=0;i<=n;i++) {
for(int j=1;j<=n;j<<=1) {
if((i^j)>n) continue;
edge[i].push_back({i^j,j*c});
}
}
还有一些细节问题:
首先看一下上面的压缩边的代码,我们发现我们是从0开始的,也就是我们增加了0节点,这是为什么呢.因为我们在下面限制了最大的中间点的数是
n
n
n,但是实际上如果没有0的话,我们需要的中间点可能是大于
n
n
n的,当然如果我们将中间点的范围限制为
2
k
−
1
(
满足
2
k
−
1
>
=
n
)
2^k-1(满足2^k-1>=n)
2k−1(满足2k−1>=n)的话,此时我们就不再需要0节点了,但是此时增加了很多结点,没有添加一个0来的更加优秀
还有假设我们直接使用堆优化的 d i j k s r t a dijksrta dijksrta的话还是会T掉,所以我们需要加上一点优化,就是当我们的 d i j k s t r a dijkstra dijkstra已经跑完我们的终点时就可以退出了,因为根据 d i j k s t r a dijkstra dijkstra的性质,跑完一个点之后是不会再更新了的,所以此时我们就已经更新完了,直接退出即可
下面是具体的代码部分:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <string.h>
#include <stack>
#include <deque>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define root 1,n,1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
#define maxn 1000000
#define ll_maxn 0x3f3f3f3f3f3f3f3f
const double eps=1e-8;
int n,m,c;int dis[maxn];
struct Node{
int v,w;
};
vector<Node>edge[maxn];
struct heapnode{
int u,d;
bool operator<(const heapnode &rhs) const {
return d>rhs.d;
}
};
int vis[maxn];int a,b;
void dij(int S) {
priority_queue<heapnode>q;
for(int i=0;i<=n;i++) dis[i]=inf;
dis[S]=0;
q.push({S,0});
while(!q.empty()) {
if(vis[b]) break;
heapnode f=q.top();q.pop();
int u=f.u;
if(vis[u]) continue;
vis[u]=1;
for(int i=0;i<edge[u].size();i++) {
int v=edge[u][i].v;
if(dis[v]>dis[u]+edge[u][i].w) {
dis[v]=dis[u]+edge[u][i].w;
q.push({v,dis[v]});
}
}
}
return ;
}
int main() {
n=read();m=read();c=read();
for(int i=1;i<=m;i++) {
int u=read(),v=read(),w=read();
edge[u].push_back({v,w});
}
for(int i=0;i<=n;i++) {
for(int j=1;j<=n;j<<=1) {
if((i^j)>n) continue;
edge[i].push_back({i^j,j*c});
}
}
a=read();b=read();
dij(a);
cout<<dis[b]<<endl;
return 0;
}