[Code+#4]最短路

部分分

直接暴力连边,跑一遍最短路。考虑到每对点都要连一条边,则边的个数至少为 \(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]);
}\
原文地址:https://www.cnblogs.com/wwlwQWQ/p/13545041.html