题意
题目链接:https://ac.nowcoder.com/acm/contest/6885/E
分析
如果直接建边,肯定会超时。那么,就要进行转化。可以设置 (32) 个虚点,分别表示点权的第 (i) 位为 (1)。对于点权 (a[i]) 如果其第 (j) 位为 (1),那么就从该点连一条权值为 (2^j) 的边到对应的虚点,并建立一条权值为 (0) 的反向边。最后跑一遍最短路即可。
证明:对于一个点,按照最短路的贪心策略,必然会选择与之相连的权值最小的出边,这样刚好满足了题目建边的原则。并且,通过虚边,可以使得两点是按 (lowbit) 建边。
代码
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<ll,int>pli;
const int N=1e5+100;
const ll inf=1e18;
ll dis[N];
int n;
vector<pli>pic[N];
priority_queue<pli,vector<pli>,greater<pli> > que;
void addedge(int u,ll w)
{
for(int i=0;i<32;i++)
{
if((w>>i)&1)
{
pic[u].pb(make_pair((1LL<<i),i));
pic[i].pb(make_pair(0,u));
}
}
}
void dij()
{
for(int i=0;i<=n+32;i++)
dis[i]=inf;
while(!que.empty()) que.pop();
dis[33]=0;
que.push(make_pair(0,33));
while(!que.empty())
{
pli now=que.top();
que.pop();
if(dis[now.second]<now.first) continue;
for(int i=0;i<pic[now.second].size();i++)
{
pli tmp=pic[now.second][i];
if(dis[tmp.second]>dis[now.second]+tmp.first)
{
dis[tmp.second]=dis[now.second]+tmp.first;
que.push(make_pair(dis[tmp.second],tmp.second));
}
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
ll a;
scanf("%d",&n);
for(int i=0;i<=n+32;i++)
pic[i].clear();
for(int i=1;i<=n;i++)
{
scanf("%lld",&a);
addedge(i+32,a);
}
dij();
if(dis[n+32]==inf) printf("Impossible
");
else printf("%lld
",dis[n+32]);
}
return 0;
}
题解:https://blog.nowcoder.net/n/13d05ab8ac22444a81fbe475de2f563e