「ABC245G」 Foreign Friends

题意

一张 \(n\) 个点,\(m\) 条边的图,每个点都有给定的颜色 \(col_i\)。给定 \(l\) 个点作为“特殊点” ,求出每个点到最近的颜色不同“特殊点” 的距离。

分析

学校里定时训练的 abc 套题,赛时直接跳了。

赛后看,结果是一个套路题。

枚举的二进制位,类似 旅行者,然后比对 \(col_i\) 二进制当前位的数字,在跑最短路时更新答案。

注意每一位都要跑两次最短路,一次以所有当前位为 \(0\) 的做起点;一次以当前位为 \(1\) 的做起点。

具体实现可以看代码(做这个题的人应该看得懂吧)。

因为枚举二进制,所以用 dijkstra 跑最短路时间复杂度有两个 \(\log\),是 \(O(m\log n\log k)\)

Code

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
#define dbg(x) cout<<#x<<": "<<x<<"\n"
inline ll read(){ll x=0,f=1;char c=getchar();while(c<48||c>57){if(c==45)f=0;c=getchar();}while(c>47&&c<58)x=(x<<3)+(x<<1)+(c^48),c=getchar();return f?x:-x;}
const ll mod=1e9+7,maxn=2e5+5,maxt=505;
ll n,m,k,l,col[maxn],ans[maxn],dis[maxn],vis[maxn];
vector<ll>ts;
vector<pair<ll,ll> >son[maxn];
priority_queue<pair<ll,ll>,vector<pair<ll,ll> >,greater<pair<ll,ll> > >q;
inline void dijkstra(ll s,ll val){
    memset(vis,0,sizeof(vis));
    while(!q.empty()){
        ll u=q.top().second;q.pop();
        if(vis[u])continue;
        vis[u]=1;
        for(auto to:son[u]){
            ll v=to.first,d=to.second;
            if(dis[u]+d<dis[v]){
                dis[v]=dis[u]+d;
                if((col[v]>>s&1)==val)ans[v]=min(ans[v],dis[v]);
                q.push({dis[v],v});
            }
        }
    }
}
inline void solve(){
    n=read(),m=read(),k=read(),l=read();
    for(ll i=1;i<=n;++i)col[i]=read();
    for(ll i=1;i<=l;++i){
        ts.push_back(read());
    }
    for(ll i=1;i<=m;++i){
        ll u=read(),v=read(),w=read();
        son[u].push_back({v,w});
        son[v].push_back({u,w});
    }
    memset(ans,0x3f,sizeof(ans));
    for(ll i=0;i<=17;++i){
        while(!q.empty())q.pop();
        memset(dis,0x3f,sizeof(dis));
        for(auto u:ts){
            if(col[u]>>i&1)q.push({0,u}),dis[u]=0;
        }
        dijkstra(i,0);
        while(!q.empty())q.pop();
        memset(dis,0x3f,sizeof(dis));
        for(auto u:ts){
            if((col[u]>>i&1)==0)q.push({0,u}),dis[u]=0;
        }
        dijkstra(i,1);
    }
    for(ll i=1;i<=n;++i){
        if(ans[i]>=1e14)printf("-1 ");
        else printf("%lld ",ans[i]);
    }
}
signed main(){
    ll t=1;
    while(t--){
        solve();
    }
    return 0;
}
posted @ 2024-12-20 15:16  run-away  阅读(12)  评论(0)    收藏  举报