首先可以发现,如果有a,b,c三个点,且a向b和c连边,b向c连边,
那么此时a直接走到c一定比以b作为中间结点更优
开始将维护数组v[i],v[i]里面存了所有二进制第i位为1的数的编号
那么可以构造一个DAG,比如刚开始的时候枚举a[1]的所有的1位,然后直接把v[i]里面的所有点的最短路更新为1到它的距离。然后让新的点入优先队列。
维护一个优先队列让距离小的优先更新,而每个v[i]最多被访问一次,即每个点到1的最多距离最多只会被更新一次
#include <bits/stdc++.h>
#define N 100010
#define ll long long
//#define Re register
#define mo 998244353
#define squ(x) ((x)*(x))
#define db double
#define cmax(a,b) a=max(a,b)
#define cmin(a,b) a=min(a,b)
#define rep(i,l,r) for(int i=l;i<=r;++i)
#define drep(i,r,l)for(int i=r;i>=l;--i)
#define pb push_back
using namespace std;
inline ll read() {
ll x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-')f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
int a[N];
vector<int> v[34];
ll dis[N];
bool vis[34];
#define Pa pair<ll,int>
#define mp make_pair
priority_queue<Pa, vector<Pa>, greater<Pa> > q;
bool cmp(int i, int j) {
return a[i] < a[j];
}
int main() {
int n = read();
rep(i, 1, n) {
dis[i] = -1;
a[i] = read();
rep(p, 0, 30)if ((a[i] >> p) & 1)v[p].pb(i);
}
rep(p, 0, 30)sort(v[p].begin(), v[p].end(), cmp);
dis[1] = 0;
q.push(mp(a[1], 1));
while (!q.empty()) {
int u = q.top().second;
q.pop();
rep(p, 0, 30) {
if ((a[u] >> p) & 1) {
if (vis[p])continue;
vis[p] = 1;
for (int &x : v[p])
{
if (dis[x] != -1)continue;
dis[x] = dis[u] + a[u] + a[x];
q.push(mp(dis[x] + a[x], x));
}
}
}
}
rep(i, 1, n)printf("%lld ", dis[i]);
return 0;
}
京公网安备 11010502036488号