Link
部分分
直接暴力连边,跑一遍最短路。考虑到每对点都要连一条边,则边的个数至少为 \(O(n^2)\) 。
正确做法
必须对建边进行优化。由异或想到二进制分解。
显然有性质
\[2^i\;Xor\;x + 2^j\;Xor\;x=(2^i+2^j)\;Xor\;x\quad\{i \neq j\}
\]
那么对于点 \(i\) ,如果我们只对与 \(i\) 二进制分解下有一位不同的点连边,每个点就至多连 \(\log_2n\) 条边,则边的数量就会降到 \(O(n\log_2n)\)
而对于点对 \((i,j)\) 总有一条双向路径使得路径长度为 \((i\;Xor\;j)\times C\)
#include<stdio.h>
#include<queue>
#include<string.h>
using namespace std;
#define N 200007
#define INF 0x3f3f3f3f
template<class T>
inline void read(T &x){
x=0;T flag=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')flag*=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
x*=flag;
}
struct Node{
int pos,dis;
Node(int pos=0,int dis=0):pos(pos),dis(dis){}
bool operator <(const Node &x) const{
return x.dis<dis;
}
};
priority_queue<Node> Q;
struct E{
int next,to,dis;
}e[N*5*10];
int head[N],cnt,dis[N];
int n,m,C;
inline int min(int x,int y){return x<y? x:y;}
inline void add(int id,int to,int d){
e[++cnt]=(E){head[id],to,d};
head[id]=cnt;
}
bool tag[N];
int main(){
// freopen("corto1.in","r",stdin);
// freopen("corto1.ans","w",stdout);
read(n),read(m),read(C);
for(int i=1;i<=m;i++){
int u,v,d;
read(u),read(v),read(d);
add(u,v,d);
}
for(int i=0;i<=n;i++)
for(int j=0;(1<<j)<=n;j++)
add(i,i^(1<<j),(1<<j)*C);
int st,ed;
read(st),read(ed);
memset(dis,INF,sizeof(dis));
dis[st]=0;
Q.push(Node(st,0));
while(!Q.empty()){
Node t=Q.top();
Q.pop();
if(tag[t.pos]) continue;
tag[t.pos]=1;
for(int i=head[t.pos];i;i=e[i].next){
int v=e[i].to;
if(dis[v]>dis[t.pos]+e[i].dis){
dis[v]=dis[t.pos]+e[i].dis;
if(!tag[v]) Q.push(Node(v,dis[v]));
}
}
}
printf("%d",dis[ed]);
}\