题目传送门
最近,牛妹天天宅在家里,真是憋死人了。他决定出去旅游。
牛妹现在正在1号点(自己家里),他决定前往n号点(牛妹想去的地方),中途可以多次经过1~n号点。
现在,已知每个点都有个权值 a i a_i ai,如果 a i & a j ≠ 0 a_i \& a_j ≠0 ai&aj=0,则i号点和j号点之间连有一条双向边,权值为 l o w b i t ( a i & a j ) lowbit(a_i \& a_j) lowbit(ai&aj)。
他想要最小化自己的行走距离,但是他计算不出来qaq。相信全牛客最聪明的你一定会吧!
Tips:
&是位运算中and的意思,lowbit(n)的值是最大的 2 x 2^x 2x,满足 2 x ∣ n 2^x | n 2x∣n。
例如 l o w b i t ( 5 ) = l o w b i t ( ( 101 ) 2 ) = 1 , l o w b i t ( 8 ) = l o w b i t ( ( 1000 ) 2 ) = 8 lowbit(5)=lowbit((101)_2)=1,\ lowbit(8)=lowbit((1000)_2)=8 lowbit(5)=lowbit((101)2)=1, lowbit(8)=lowbit((1000)2)=8。
输入描述:
本题有多组数据。
第一行,输入一个数T,表示数据组数。
接下来2*T行,先输入一个数n,再输入n个数,第i个数表示
a
i
a_i
ai。
输出描述:
对于每组数据,输出最小的行走距离。
如果无法从1号点到达n号点,则输出“Impossible”(不含引号)。
示例一
输入
2
6
2 3 5 8 13 21
12
1 2 3 4 5 6 7 8 9 10 11 12
输出
3
5
示例二
输入
5
3
1 2 3
4
177 188 199 211
2
1 2
4
1 1 1 1
5
1 2 4 8 16
输出
1
1
Impossible
1
Impossible
备注:
数据保证 1 ≤ T ≤ 5 , 1 ≤ n ≤ 1 0 5 1≤T≤5,1≤n≤10^5 1≤T≤5,1≤n≤105, 1 ≤ T ≤ 5 , 1 ≤ n ≤ 1 0 5 , 1 ≤ a i < 2 32 1\le T\le 5, 1\le n\le 10^5, 1\le a_i < 2^{32} 1≤T≤5,1≤n≤105,1≤ai<232
思路
建图,然后跑一遍Dijkstra,重点部分在建图的优化:
n 2 n^2 n2建边会T掉,考虑二进制优化建边:
因为 a i < 2 32 a_i<2^{32} ai<232,所以不妨设置每一个数位为一个虚点,连接从 a i a_i ai到虚点的双向边,从 a i a_i ai到虚点的边权为数位对应的基数,从虚点到 a i a_i ai的边权为0。
举个例子说明,假如当前有两个点对应的权值为 a 1 = 1 , a 2 = 5 a_1=1,a_2=5 a1=1,a2=5, a 1 a_1 a1对应的二进制表示为1, a 2 a_2 a2对应的二进制表示为101,建边如下图所示,然后以1为起点跑一个 Dijkstra 求dis[n]就可以了。

代码
#include <bits/stdc++.h>
using namespace std;
#define MAXN 100100
#define INF (ll)0x3f3f3f3f3f3f3f3f
typedef long long ll;
ll arr[MAXN];
struct node{
ll c;
int to,nx;
}eages[MAXN*40];
struct point{
int pos;
ll dis;
};
bool operator < (point a,point b){
return a.dis>b.dis;
}
priority_queue<point>q;
int head[MAXN];
int tot;
ll dis[MAXN];
bool vis[MAXN];
ll lowbit(ll x){
return x&(-x);
}
void init(){
memset(head,-1,sizeof(head));
tot=0;
while (!q.empty()){
q.pop();
}
}
void add(int u,int v,ll cost){
eages[++tot].nx=head[u];
eages[tot].to=v;
eages[tot].c=cost;
head[u]=tot;
}
//普通Dijkstra
ll dijstra(int s,int n){
memset(dis,INF,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[s]=0;
int mark;
ll mi;
for (int k=0;k<n;k++){
mi=INF;
for (int i=0;i<=n;i++){
if (!vis[i] && dis[i]<mi){
mi=dis[i];
mark=i;
}
}
vis[mark]=true;
for (int i=head[mark];i!=-1;i=eages[i].nx){
int v=eages[i].to;
if (dis[mark]+eages[i].c<dis[v]){
dis[v]=dis[mark]+eages[i].c;
}
}
}
return dis[n];
}
//堆优化的Dijkstra
ll dijstra2(int s,int n){
memset(dis,INF,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[s]=0;
q.push(point{s,0LL});
while (!q.empty()){
point t=q.top();
q.pop();
int mark=t.pos;
for (int i=head[mark];i!=-1;i=eages[i].nx){
int v=eages[i].to;
if (dis[mark]+eages[i].c<dis[v]){
dis[v]=dis[mark]+eages[i].c;
q.push(point{v,dis[v]});
}
}
}
return dis[n];
}
int main (){
int cases,n;
scanf("%d",&cases);
while(cases--){
scanf("%d",&n);
init();
for (int i=1;i<=n;i++){
scanf("%lld",&arr[i]);
ll j=0;
while (j<=32){
ll t = (1LL<<j);
ll c = arr[i] & t;
if (c){
add(i+32,j,t);
add(j,i+32,0);
}
j++;
}
}
ll ans=dijstra2(1+32,n+32);
ans==INF?puts("Impossible"):printf("%lld\n",ans);
}
return 0;
}
牛妹计划从家出发,通过计算最小行走距离到达目的地。利用Dijkstra算法,结合二进制优化建图,解决特定条件下的路径寻找问题。
1382

被折叠的 条评论
为什么被折叠?



