基里巴斯

題目描述

由於島嶼衆多,政府在所有的島嶼之間均設有固定的經濟航線連接。對於編號爲 u , v 的島嶼 ( u , v ) 對,連接兩島的單向航線通行費用是 ( u ⊕ v ) × K 。其中, ⊕ 表示異或( X o r )。
泛美航空公司開設了 M 條往返於各島的單向航線,每條航線均有一個固定的通行費用以期以更低的價格佔有更多的用戶羣。
於是,他打算從 S 飛到城市 T 。他希望得知從 S 到城市 T 的最小費用。

輸入

輸入第一行包含三個整數 N , M , K ,含義如題所示。
接下來 M 行,每行三個整數 u i , v i , w i 。描述一條單向航線。
最後一行兩個整數 S , T ,表示想要調研的起點和終點。

輸出

輸出包含一個整數,爲從 S 到城市 T 的最小費用。

題解

其實異或一般都涉及到拆位,但很遺憾,這題一看並不可以。思考這題的最大問題是什麼,是完全圖的邊太多了,最短路會T。考慮減少邊數,即一個點不需要連所有邊,也能達到連所有邊同樣的效果。
假如現在有一條邊i->j,它的代價一定是一個01串,其中有貢獻的只有那些1,那麼我們就可以把兩點間的貢獻分散到整張圖裏面,i 只往i(1k)|(i(1k))n 連邊,邊權恰好爲1k0 也會出現在新的圖裏面,這樣才能保證連通的正確性。
換句話說,這就像二進制拆分,我們反過來想象我們只是拿走了不是二進制拆分的邊,因爲圖本身是完全的,原來的圖中總存在着等價的二進制組合起來的邊。
再換句話說,因爲每個點都往外連了1,10,100,1000…的邊,任意兩點間總能找到恰好等於uv 的邊集。
以上是本蒟蒻欺騙說服自己這個解法是正確的心路歷程。

代碼

#include <bits/stdc++.h>
#define maxn 1000005
#define MAXN 6000005
#define INF 0x3f3f3f3f
#define eps 1e-7
typedef long long LL;
using namespace std;
struct EDGE{
   int u,v,w,nxt;
}e[MAXN];
struct NODE{
    int u,d;
    bool operator < (const NODE &rhs)const{
        return d>rhs.d;
    }
};
int n,m,k,cnt,S,T,head[maxn],vis[maxn];
LL dis[maxn];
void add(int u,int v,int w){
    e[++cnt]=(EDGE){u,v,w,head[u]};
    head[u]=cnt;
}
priority_queue<NODE> Q;
void Djkstra(int s){
    memset(dis,0x3f,sizeof dis);
    Q.push((NODE){s,0});
    dis[s]=0;
    while(!Q.empty()){
        int u=Q.top().u; Q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=head[u];i!=-1;i=e[i].nxt){
            int v=e[i].v,w=e[i].w;
            if(dis[v]>dis[u]+w){
                dis[v]=dis[u]+w;
                Q.push((NODE){v,dis[v]});
            }
        }
    }
}
int main(){
    memset(head,-1,sizeof head);
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
    }
    scanf("%d%d",&S,&T);
    for(int i=1;i<=n;i++){
        for(int j=0;j<=17;j++){
            if((i^(1<<j))<=n) add(i,i^(1<<j),(1<<j)*k);
        }
    }
    Djkstra(S);
    printf("%d\n",dis[T]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章