*牛妹游历城市【最短路建虚点】

题意

题目链接: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

原文地址:https://www.cnblogs.com/1024-xzx/p/13511839.html