1006 最短路 dijkstra 异或运算变化的精简操作

 链接:https://ac.nowcoder.com/acm/contest/26077/1006
来源:牛客网

题目描述

企鹅国中有NNN座城市,编号从111到NNN。
对于任意的两座城市iii和jjj,企鹅们可以花费(i  xor  j)∗C(i\,\,xor\,\, j)*C(ixorj)C的时间从城市iii走到城市jjj,这里CCC为一个给定的常数。
当然除此之外还有MMM条单向的快捷通道,第i条快捷通道从第FiF_iFi个城市通向第TiT_iTi个城市,走这条通道需要消耗ViV_iVi的时间。
现在来自Penguin Kingdom University的企鹅豆豆正在考虑从城市AAA前往城市BBB最少需要多少时间?


输入描述:

输入第一行包含三个整数N,M,C,表示企鹅国城市的个数、快捷通道的个数以及题面中提到的给定的常数C。
接下来的M行,每行三个正整数F
i
,T
i
,V
i
(1≤F
i
≤N,1≤T
i
≤N,1≤V
i
≤100),分别表示对应通道的起点城市标号、终点城市标号和通过这条通道需要消耗的时间。
最后一行两个正整数A,B(1≤C≤100),表示企鹅豆豆选择的起点城市标号和终点城市标号。

输出描述:

输出一行一个整数,表示从城市 A 前往城市 B 需要的最少时间。
示例1

输入

复制
4 2 1
1 3 1
2 4 4
1 4

输出

复制
5

说明

直接从 1 走到 4 就好了。   
示例2

输入

复制
7 2 10
1 3 1
2 4 4
3 6

输出

复制
34

说明

先从 3 走到 2 ,再从 2 通过通道到达 4 ,再从 4 走到 6。   

备注:

分析

图片太小了,看了别人的提交才知道数据范围是1e7。。。

这道题加入了 i ^ j 为两点距离。如果暴力跑1e10会超时。所以需要根据位运算的特性精简边数。

i 和 j 的差别就是 1 的位置和数量。

首先先忽略直通路径

对于每对i ,j来说。从i 到 j 有两种方式

一种是i 经过其它点k 到达 j 花费是 i ^ k + k ^ j

一种是i 直接到达 j 花费是i ^ j

抽象点说:

1.k包含 i 和 j 都拥有的位置,那 i ^ k 后会把这个位置删掉,但是为了走到 j 又得再次把这个位置补上。

2.k包含 i 和 j 都没有的位置,那 i ^ k 后会把这个位置补上,但是为了走到 j 又得再次把这个位置删掉。

但实际 i 走到 j 只用走 i 和 j 的不同的位置。

对最短路长度的贡献,取决于两者不同1的位置和数量,一旦引入其它点会导致位数贡献变多,不是最优

现在把贡献拆开,i 和 j 的位置差别可能不止一位。要使i 变成 j ,i 就需要把自己连向和自己只差一位的点,这样经过有限次变动,i 就可以变成 j。每次移动都是 i 和 j 不同的位数 所以答案不会变差。

按照这样精简边数就可以跑最短路。

//-------------------------代码----------------------------

//#define int ll
const int N = 1e7+10;
int n,m,c;

int e[N],ne[N],w[N],h[N],idx;
int dist[N];bool vis[N];
void add(int a,int b,int c) {
    e[idx] = b,ne[idx] = h[a],w[idx] = c,h[a] = idx++ ;
}
int st,ed;
void dij() {
    ms(dist,0x3f);
    ms(vis,0);
    priority_queue<pii,V<pii>,greater<pii>> q;
    q.push({0,st});
    dist[st] =  0;
    while(q.size()) {
        auto t = q.top();q.pop();
        int ver = t.second,W = t.first;
        if(vis[ver]) continue;
        vis[ver] = 1;
        for(int i = h[ver];~i;i=ne[i]) {
            if(vis[e[i]]) continue;
            if(dist[e[i]] > W + w[i]) {
                dist[e[i]] = dist[ver] + w[i]; 
                q.push({dist[e[i]],e[i]});
            }
        }
    }
}

void solve()
{
    cin>>n>>m>>c;
    ms(h,-1);
    fo(i,1,m) {
        int x,y,z;cin>>x>>y>>z;
        add(x,y,z);
    }
    fo(i,0,n) {
        for(int j = 1;j<=n;j<<=1) {
            if((i ^ j) > n)continue;
            add(i,i^j,j*c);
        }
    }
    cin>>st>>ed;
    dij();
    cout<<dist[ed]<<endl;
}
void main_init() {}
signed main(){
    AC();clapping();TLE;
    cout<<fixed<<setprecision(12);
    main_init();
//  while(cin>>n,n)
//  while(cin>>n>>m,n,m)
//    int t;cin>>t;while(t -- )
    solve();
//    {solve(); }
    return 0;
}

/*样例区


*/

//------------------------------------------------------------

 

posted @ 2022-08-17 22:29  er007  阅读(116)  评论(0编辑  收藏  举报