2024“钉耙编程”中国大学生算法设计超级联赛(3)1008比特跳跃

题目大意 :给出n个城市m条联通两个城市的无向边,从uivi需要耗费ti的时间,你也可以选择进行一次比特跳跃,耗费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 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();  
}
posted @   yoez123  阅读(92)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示