奇怪的最短路

Description

给定长度为 n n n 的数列 a a a ,如果 a i ​   &   a j ​ ≠ 0 a_i​ \ \& \ a_j ​\ne 0 ai & aj=0(按位与),则在 i , j i,j i,j 之间存在一条长度为 a i ​ + a j a_i​ + a_j ai+aj​ 的边,求 1 1 1 至所有点的最短路。

Input

第一行一个正整数 n n n
接下来一行 n 个整数 a 1 ​ ∼ a n ​ a_1​∼a_n​ a1an

Output

输出一行 n 个整数,第 i 个为 1 到 i 的最短路长度。不能到达输出 − 1 -1 1

Sample Input

5
1 5 6 7 8

Sample Output

0 6 17 8 -1

HINT

对于 100   % 100 \ \% 100 % 的数据, 1 ≤ n ≤ 1 0 5 , 0 ≤ a i ​ ≤ 2 30 1≤n≤10^5, 0≤a_i​≤2^{30} 1n105,0ai230

解题思路

本题关键在于建图,对于 1 − n 1-n 1n 看作 n n n 个节点,此外由于 0 ≤ a i ​ ≤ 2 30 0≤a_i​≤2^{30} 0ai230,所以 a i a_i ai 在二进制下最多有 31 31 31 位,看作 31 31 31 个节点,若 a i a_i ai 二进制值的第 j j j 位是 1 1 1 ,则对 a i a_i ai j j j 建边,边权为 a i a_i ai ,然后跑一次最短路即可。
PS:注意边的数量要开够

A C   c o d e AC \ code AC code

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 99;

inline int read()
{
    char c = getchar();
    int x = 0;
    bool f = 0;
    for (; !isdigit(c); c = getchar())
    {
        f ^= !(c ^ 45);
    }
    for (; isdigit(c); c = getchar())
    {
        x = (x << 1) + (x << 3) + (c ^ 48);
    }
    if (f)
    {
        x = -x;
    }
    return x;
}

inline void write(int x)
{
    if (x < 0)
    {
        putchar('-');
        x = -x;
    }
    if (x > 9)
    {
        write(x / 10);
    }
    putchar(x % 10 + '0');
}

long long n, m;
array<long long, maxn> dis;
bool vis[maxn];
long long x[maxn];

struct edge
{
    int to, w;
};
vector<edge> d[maxn];

struct node
{
    long long i, dis;
    bool operator<(const node tmp) const
    {
        return tmp.dis < dis;
    }
};

void add(int a, int b, int w)
{
    d[a].push_back({b, w});
}

void Dijkstra()
{
    dis.fill(0x3f3f3f3f3f);
    priority_queue<node> q;
    dis[1] = 0;
    q.push({1, 0});
    while (!q.empty())
    {
        node tmp = q.top();
        q.pop();
        int t = tmp.i;
        if (vis[t])
            continue;
        vis[t] = 1;
        for (int i = 0; i < d[t].size(); i++)
        {
            if (dis[t] + d[t][i].w < dis[d[t][i].to])
            {
                dis[d[t][i].to] = dis[t] + d[t][i].w;
                q.push({d[t][i].to, dis[d[t][i].to]});
            }
        }
    }
}

signed main()
{
    n = read();
    for (int i = 1; i <= n; ++i)
    {
        x[i] = read();
        for (int j = 0; j <= 30; j++)
            if ((x[i] & (1 << j)) != 0)
                add(i, n + j + 1, x[i]), add(n + j + 1, i, x[i]);
    }
    Dijkstra();
    for (int i = 1; i <= n; ++i)
    {
        write((dis[i] == 0x3f3f3f3f3f ? -1 : dis[i]));
        putchar(' ');
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值