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。i
接下来的M行,每行三个正整数F
,Ti
,Vi
(1≤Fi
≤N,1≤Ti
≤N,1≤Vi
≤100),分别表示对应通道的起点城市标号、终点城市标号和通过这条通道需要消耗的时间。
最后一行两个正整数A,B(1≤C≤100),表示企鹅豆豆选择的起点城市标号和终点城市标号。
输出描述:
输出一行一个整数,表示从城市 A 前往城市 B 需要的最少时间。
备注:
分析
图片太小了,看了别人的提交才知道数据范围是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; } /*样例区 */ //------------------------------------------------------------