ABC245 G Foreign Friends

ABC245 G Foreign Friends

题目描述

N N N个人和 K K K个国家,每个人属于唯一的一个国家。第 i i i个人所属的国家为 A i A_i Ai。其中,有 L L L个受欢迎的人,分别是 B 1 , B 2 , … , B L B_1,B_2,\dots ,B_L B1,B2,,BL。一开始,没有人相互是朋友。

对于 M M M对人,小 T T T可以花费 C i C_i Ci的代价,使 U i U_i Ui V i V_i Vi成为朋友。

现在,对于每个 i ( 1 ≤ i ≤ N ) i(1\leq i\leq N) i(1iN),问 i i i是否能和另外一个国家的某个受欢迎的人成为直接或间接的朋友。所谓间接的朋友,是指他们之间有一条朋友关系的路径,路径上的每条边都表示一对朋友关系。 如果可以,输出小 T T T要花费的最小的代价,否则输出 − 1 -1 1

题解

这是一道最短路问题,我们可以使用Dijkstra。

每个点都跑一遍显然不行,所以我们可以考虑让每个受欢迎的人去与每个人交朋友。但如果每个受欢迎的人还是会TLE,所以要继续优化。

我们把所有受欢迎的人放在优先队列中跑最短路,并记录他们所属的国家。要保证每个人只会被每个国家更新一次。

我们需要在从优先队列中取出一个人的信息后再更新这个人。对每个人维护两个值:最小值和次小值。并且最小值和次小值所来自的受欢迎的人所属的国家不同。因为队列是优先队列,所以每个人第一次取出的值为最小值,第二次取出的值为次小值。

在输出时,如果最小值所来自的国家与这个人的国家不同,则输出最小值;否则因为次小值所来自的国家与最小值的不同,所以次小值所来自的国家与这个人所属的国家一定不同,输出次小值。

因为每个人只会被更新两次,用到优先队列,所以时间复杂度为 O ( n l o g   n ) O(nlog \ n) O(nlog n)

code

#include<bits/stdc++.h>
using namespace std;
int n,m,k,t,x,y,z,tot,a[100005],b[100005],d[200005],l[200005],r[200005],us[100005];
long long tw[200005];
struct node{
    int c1,c2;
    long long v1,v2;
}w[100005];
struct vt{
    int x,c;
    long long dis;
    bool operator<(const vt ax)const{
        return dis>ax.dis;
    }
};
priority_queue<vt>q;
void add(int xx,int yy,int zz){
    l[++tot]=r[xx];d[tot]=yy;r[xx]=tot;tw[tot]=zz;
}
void dd(){
    while(!q.empty()){
        vt u=q.top();q.pop();
        if(us[u.x]<2&&w[u.x].c1!=u.c){
            if(!us[u.x]){
                w[u.x].c1=u.c;w[u.x].v1=u.dis;
            }
            else{
                w[u.x].c2=u.c;w[u.x].v2=u.dis;
            }
            ++us[u.x];
            for(int i=r[u.x];i;i=l[i]){
                q.push((vt){d[i],u.c,u.dis+tw[i]});
            }
        }
    }
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&k,&t);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=t;i++){
        scanf("%d",&b[i]);
        q.push((vt){b[i],a[b[i]],0});
    }
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);add(y,x,z);
    }
    dd();
    for(int i=1;i<=n;i++){
        if(a[i]==w[i].c1){
            if(w[i].c2) printf("%lld ",w[i].v2);
            else printf("-1 ");
        }
        else{
            if(w[i].c1) printf("%lld ",w[i].v1);
            else printf("-1 ");
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值