牛客IOI周赛26-普及组 D 最短路
题意:
给定 n 个点的值,两个点之间连通当且仅当两点的值按位与运算之后答案不为 0,边长为 a i + a j a_i+a_j ai+aj ,求从点 1 开始,到各点的最短路。
思路:
看起来像是最短路模板题 (实际也确实差不多) ,但是如果将每个点都判断一次是否可以连边的话会超时,所以只要想办法将
O
(
n
2
)
O(n^2)
O(n2) 的连边复杂度优化就可以套模板直接做。
仔细思考 (看题解) 可发现,要想两点能连边,只要两点的二进制上的某一位同时为 1 即可。
因此我们可以考虑建 30 个虚点,而 a i a_i ai 与虚点连边的条件是:
a i a_i ai 与 ( 1 < < k ) (1<<k) (1<<k) 按位与运算的结果不为 0 (k 代表第 k 个虚点),边权为 a i a_i ai
for (int i = 1;i <= n;i++)
{
for (int j = 0;j <= 30;j++)
{
if ((a[i] & (1ll << j)) != 0)
{
add(i, n + j + 1, a[i]);
add(n + j + 1, i, a[i]);
}
}
}
将连边的复杂度降到 O ( n ) O(n) O(n) ,直接套 dij 模板即可。
代码:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
#include<vector>
#include<math.h>
#include<queue>
#include<stack>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double dd;
typedef pair<int, int> pii;
typedef pair<dd, dd> pdd;
const int MAXN = 100100;
const int inf = 1e9 + 7;
const ll llinf = 1e17 + 7;
const int MAXM = 5005000;
struct EDGE {
int u, v, nxt;
ll w;
}e[MAXM];
int head[MAXN], cnt, m, s, vis[MAXN];
int n;
ll dis[MAXN];
struct NODE {
ll w;
int now;
bool operator < (const NODE& x)const {
return w > x.w;
}
};
priority_queue<NODE>q;
void add(int u, int v, ll w)
{
e[++cnt].u = u;
e[cnt].v = v;
e[cnt].w = w;
e[cnt].nxt = head[u];
head[u] = cnt;
}
void dij()
{
for (int i = 1;i <= n;i++) dis[i] = llinf;
dis[s] = 0;
q.push({ 0,s });
while (q.size())
{
NODE x = q.top();
q.pop();
int u = x.now;
if (vis[u]) continue;
vis[u] = 1;
for (int i = head[u];i;i = e[i].nxt)
{
int v = e[i].v;
if (dis[v] > dis[u] + e[i].w)
{
dis[v] = dis[u] + e[i].w;
q.push({ dis[v],v });
}
}
}
}
ll a[MAXN];
int main()
{
scanf("%d", &n);
for (int i = 1;i <= n;i++) scanf("%lld", &a[i]);
for (int i = 1;i <= n;i++)
{
for (int j = 0;j <= 30;j++)
{
if ((a[i] & (1ll << j)) != 0)
{
add(i, n + j + 1, a[i]);
add(n + j + 1, i, a[i]);
}
}
}
int nn = n;
n += 31;
s = 1;
dij();
for (int i = 1;i <= nn;i++)
{
if (dis[i] >= llinf) printf("-1 ");
else printf("%lld ", dis[i]);
}
}
总结:
分层图思想不够熟练,还需多做题