2017多校第9场 HDU 6166 Senior Pan 堆优化Dij

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6166

题意:给你一个有向图,然后给你k个点,求其中一个点到另一个点的距离的最小值。

解法:枚举二进制位按照标号当前位为1 和当前位为0分为两个集合,每次求解两个集合之间的最短路即可覆盖到所有的点对。时间复杂度20*dijstla时间,这样做的正确性在哪?显然我们需要的答案至少有一个二进制位不同,那么这样求解肯定可以找到正确答案,事实上还可以随机分组emmmm。。。

 

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 100010;
const LL inf = 0x3f3f3f3f3f3f3f3f;
struct edge{
    int to,val,next;
}E[maxn];
int head[maxn],edgecnt,a[maxn];
bool vis[maxn];
LL dis[maxn];
void initedge(){
    edgecnt=0;
    memset(head,-1,sizeof(head));
}
void add(int u, int v, int w){
    E[edgecnt].to=v,E[edgecnt].val=w,E[edgecnt].next=head[u],head[u]=edgecnt++;
}
struct node{
    int x;
    LL step;
    node(int x, LL step):x(x),step(step){}
    bool operator < (const node &rhs) const{
        return step>rhs.step;
    }
};
priority_queue<node>q;
LL Dijstra(){
    while(!q.empty()){
        node now=q.top(); q.pop();
        if(vis[now.x]) return now.step;
        int u=now.x;
        for(int i=head[u]; ~i; i=E[i].next){
            int to = E[i].to;
            if(dis[to]>dis[u]+E[i].val){
                dis[to]=dis[u]+E[i].val;
                q.push(node(to,dis[to]));
            }
        }
    }
    return inf;
}
void init(){
    memset(vis, 0, sizeof(vis));
    for(int i=0; i<maxn; i++) dis[i]=inf;
    while(!q.empty()) q.pop();
}
LL work(int k)
{
    LL ans = inf;
    for(int i=0; i<20; i++){
        init();
        for(int j=1; j<=k; j++){
            if(a[j]&(1<<i)){
                q.push(node(a[j],0)),dis[a[j]]=0;
            }
            else{
                vis[a[j]]=1;
            }
        }
        ans = min(ans, Dijstra());
        init();
        for(int j=1; j<=k; j++){
            if(a[j]&(1<<i)){
                vis[a[j]]=1;
            }
            else{
                q.push(node(a[j],0)),dis[a[j]]=0;
            }
        }
        ans = min(ans, Dijstra());
    }
    return ans;
}
int T,n,m,k,ks;
int main()
{
    ks = 0;
    scanf("%d", &T);
    while(T--)
    {
        initedge();
        scanf("%d %d",&n,&m);
        for(int i=1; i<=m; i++){
            int u, v, w;
            scanf("%d %d %d", &u,&v,&w);
            add(u, v, w);
        }
        scanf("%d", &k);
        for(int i=1; i<=k; i++) scanf("%d", &a[i]);
        LL ans = work(k);
        printf("Case #%d: %lld\n", ++ks, ans);
    }
    return 0;
}

 

posted @ 2017-08-23 10:56  zxycoder  阅读(262)  评论(0编辑  收藏  举报