题意
一张 \(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;
}
标签:read,ll,while,Foreign,vis,dijkstra,ABC245G,Friends,dis
From: https://www.cnblogs.com/run-away/p/18619355