Dij--P4366 [Code+#4]最短路

*传送

题意: 给定$n$个点,求从$s$到$t$的最短路径,其中有两种走法(可以混搭):一种是走给定的m有向边($u_i$,$v_i$,$w_i$);另一种可以由任意点x到任意点y,其费用是$c$ $\times$ ($x$ $xor$ $y$)

朴素的建法是$O$ ($n^2$ $\times$ $m$)的.然而事实上一个从$x$$\to$ $y$权值为$w$的边是可以被其他边取代的,我们可以把$x$拆成二进制,一位一位的修改最终到达y,此时经过的权值显然也是$w$。

举个例子:

假设我们要从 $001_2$​ 到 $010_2$​,我们要花费 $2^0$ +$ 2^1$ 的费用; 但是,最短路有一个 优越的性质,我们可以把边拆开来,可以先从 $001_2$​ 到$ 000_2$,再从$ 000_2$​ 到$ 010_2$,费用是一样的。

也就是说,对于一个点$x$,我们只需要让他和$x$ $\times$ $2^k$连边即可,这样就优化为$O$($nlogn$+$m$)了,跑一遍$dij$就好了。

代码:

 1 #include <iostream>
 2 #include <cstring>
 3 #include <cstdio>
 4 #include <algorithm>
 5 #include <queue> 
 6 #include <cmath>
 7 using namespace std;
 8 int n,m,c,st,sd,x,y,val;
 9 struct node{
10     int to,next,w;
11 }ed[5000005];
12 int dis[5000005],vis[5000005],head[5000005],cnt;
13 inline void add(int u,int v,int w){
14     ed[++cnt].next=head[u];
15     ed[cnt].to=v;
16     ed[cnt].w=w;
17     head[u]=cnt;
18 }
19 inline int read(){
20     int x = 1,a = 0;
21     char ch = getchar();
22     while(ch < '0' || ch > '9'){
23         if(ch == '-')x = -1;
24         ch = getchar();
25     }
26     while(ch <= '9'&&ch >= '0'){
27         a = a * 10 + ch - '0';
28         ch = getchar();
29     }
30     return x*a;
31 }
32 priority_queue<pair<int,int> > q;
33 inline void Dij(int s){
34     q.push(make_pair(0,s));memset(vis,0,sizeof(vis));memset(dis,63,sizeof(dis));dis[s]=0;
35     while (!q.empty()){
36         int x = q.top().second;
37         q.pop();
38         if (vis[x]) continue;
39         vis[x]=1;
40         for (register int i = head[x];i;i=ed[i].next){
41             int to = ed[i].to;
42             if (dis[to]>dis[x]+ed[i].w){
43                 dis[to]=dis[x]+ed[i].w;
44                 q.push(make_pair(-dis[to],to));
45             }
46         }
47     }
48     return;
49 }
50 int main(){
51     n=read();m=read();c=read();
52     for (int i = 1;i <= m;i++){
53         x=read(),y=read(),val=read();
54         add(x,y,val);
55     }
56     int lgn = floor(log2(n)) + 1;
57     n = (1 << lgn) - 1;
58     for (register int i = 1;i <= n;i++)
59         for (register int j = 0;j < lgn;j++)
60             add(i,i^(1<<j),(1<<j)*c);
61     st=read(),sd=read();
62     Dij(st);
63     cout<<dis[sd];
64 }

 

posted @ 2020-04-19 18:39  小又又  阅读(179)  评论(1编辑  收藏  举报