题目大意 :给出n个城市m条联通两个城市的无向边,从\(u_i\)到\(v_i\)需要耗费\(t_i\)的时间,你也可以选择进行一次比特跳跃,耗费k*(u|v)的时间
思路 :不难发现,比特跳跃最多跳跃一次。
证明:假设使用两次比特跳跃,a->b,c->d,那么权值为 k(a|b+c|d) ,不如直接从a->d,权值为 k(a|d),因为a|b+c|d >= min(a,b)+min(c,d)>=a+d>=a|d
所以1号点到每个点的最短路径上肯定最多只有一个比特跳跃的边
对于一个点,如果他想要从某个点比特跳跃跳到当前点,一定是从他的子集跳过来 不然答案肯定比从1号点跳过来更劣
由于刚开始的图可能不一定是连通图 所以我们要先让1号点连接其他点将图变成连通图。然后跑一遍最短路。
这个时候,已经初步得到了1号点跟其他点的最短路径
但是不一定是最优的
我们还需要将当前点的最短路跟从他子集中的最短路比特跳跃过来的值进行比较。
处理1->(走)y+y->(跳)x \(\leqslant\) 1->(初步最短路)x的情况;
在上述基础上再跑一次最短路就是最优答案
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
const int maxn=5e6+10;
int n,m,s,cnt=0;
const int inf=1e18;
int dis[maxn],h[maxn],to[maxn],val[maxn],nxt[maxn],vis[maxn],mind[maxn];
struct node{
int v,w;
friend bool operator <(node a,node b){
return a.w>b.w;
}
};
priority_queue<node>q;
void add(int a,int b,int c){
to[++cnt]=b;
val[cnt]=c;
nxt[cnt]=h[a];
h[a]=cnt;
}
void dijkstra(){
for(int i=0;i<=n;i++)dis[i]=inf;
dis[s]=0;
node tmp;
tmp.v=s;tmp.w=0;q.push(tmp);
while(!q.empty()){
int u=q.top().v;q.pop();
if(vis[u])continue;vis[u]=1;
for(int i=h[u];i;i=nxt[i]){
if(dis[to[i]]>dis[u]+val[i]){
dis[to[i]]=dis[u]+val[i];
tmp.w=dis[to[i]];
tmp.v=to[i];
q.push(tmp);
}
}
}
}
void solve(){
int k;
cnt=0;
cin>>n>>m>>k;
s=1;
memset(h,0,sizeof(h));
for(int i=0;i<=n;i++)vis[i]=0;
for(int i=1,u,v,w;i<=m;i++){
cin>>u>>v>>w;
add(u,v,w);
add(v,u,w);
}
for(int i=2;i<=n;i++){
add(1,i,(i|1)*k);
add(i,1,(i|1)*k);
}
dijkstra();
for (int i = 0; i <= n; i++) mind[i] = dis[i] , vis[i] = 0;
for (int i = 2; i <= n; i++){
for (int j = 0; (1<<j) <= n; j++) if ((i>>j)&1){
mind[i] = min(mind[i],mind[i^(1<<j)]);
}
dis[i] = min(dis[i],mind[i]+k*1ll*i);
}
dijkstra();
for(int i=2;i<=n;i++){
cout<<dis[i]<<' ';
}
}
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int _=1;
cin>>_;
while(_--)solve();
}
标签:钉耙,cnt,比特,int,短路,2024,maxn,1008,跳跃
From: https://www.cnblogs.com/yoez123/p/18333272