problem
给定一个 n n n 个点的完全图,边 ( u , v ) (u,v) (u,v) 的权是 ( u x o r v ) × C (u\;\mathrm{xor}\;v)\times C (uxorv)×C, C C C 是一个常数。
在这个完全图的基础上再加上 m m m 条有向边,问从 S S S 点到 T T T 点的最短路是多少。
数据范围: n ≤ 1 0 5 n\le 10^5 n≤105, m ≤ 5 × 1 0 5 m\le5\times 10^5 m≤5×105。
solution
建边的思路应该比较好想。
对于每个点,我们向其二进制中各个位反转后的点连一条 2 i × C 2^i\times C 2i×C 的边( i i i 是当前位)。
比如,对于 011 011 011,我们分别向 111 111 111, 001 001 001 和 010 010 010 连边。
那么,如果要从 011 011 011 转移到 101 101 101,可以看成是 011 → 111 → 101 011\rightarrow111\rightarrow101 011→111→101,花费了 2 C 2C 2C 的代价,与连边是吻合的。
这样总共有 O ( n log n + m ) O(n\log n+m) O(nlogn+m) 条边,我开了 O 2 O2 O2 才跑过的。
code
#include<bits/stdc++.h>
using namespace std;
namespace IO{
const int Rlen=1<<22|1;
char buf[Rlen],*p1,*p2;
inline char gc(){
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T Read(){
char c=gc();T x=0,f=1;
while(!isdigit(c)) f^=(c=='-'),c=gc();
while( isdigit(c)) x=(x+(x<<2)<<1)+(c^48),c=gc();
return f?x:-x;
}
inline int in() {return Read<int>();}
}
using IO::in;
const int N=1e5+5,M=5e6+5;
int n,m,C,t,S,T;
int first[N],v[M],w[M],nxt[M];
void add(int x,int y,int z){
nxt[++t]=first[x],first[x]=t,v[t]=y,w[t]=z;
}
void build(){
for(int i=0;i<=n;++i)
for(int j=0;j<17;++j)
add(i,i^(1<<j),C*(1<<j));
}
int d[N];
typedef pair<int,int> pii;
priority_queue<pii>Q;
void dijkstra(int S){
memset(d,127/3,sizeof(d));
Q.push(pii(0,S)),d[S]=0;
while(!Q.empty()){
int x=Q.top().second;Q.pop();
for(int i=first[x];i;i=nxt[i]){
int to=v[i];
if(d[to]>d[x]+w[i]) d[to]=d[x]+w[i],Q.push(pii(-d[to],to));
}
}
}
int main(){
n=in(),m=in(),C=in();
for(int i=1,x,y,z;i<=m;++i){
x=in(),y=in(),z=in(),add(x,y,z);
}
build();
S=in(),T=in(),dijkstra(S);
printf("%d\n",d[T]);
return 0;
}