题目传送门:http://acm.csu.edu.cn/csuoj/problemset/problem?pid=2005。
题目大意:一个国家里有n个城市,有s个特殊的城市,这n个城市之间有m条无向路。给出q次询问,每次要你求出离城市x最近的特殊城市,如果有多个相同距离的特殊城市,则按城市编号升序输出这几个城市。
题目思路:这题是今年湖南省赛的题目,很遗憾,在现场没能把这题做出来。现在想起来当时真的是石乐志,居然想把s个特殊城市和其他城市的最短距离全部处理出来,再储存起来,这么写真的不T都没天理。赛后去问了学长,他们的做法是先建立一个超级源点,向s个特殊城市连权值为0的边,从超级源点开始跑一遍dijkstra,在跑的时候记录一下每个点最近的特殊城市是哪几个,最后查询直接离线查询即可。也是因为用bitset用的少的原因,后来补题的时候我是直接想用set去维护最近的特殊城市,妥妥的MLE了(128MB的内存都救不了我...唉...),后来又问了一下那些大佬,才知道他们是用bitset维护的,开个1000大小的bitset,二进制的每一位代表当前位的特殊城市是否是离这个点最近的特殊城市,这样一边做dijkstra一边维护最近的特殊城市就行了。最后查询时再用Ans数组储存下,sort一下再输出即可。具体看下面代码。
AC代码如下:
#include <set>
#include <queue>
#include <cmath>
#include <bitset>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define INF 0x3f3f3f3f
#define FIN freopen("in.txt","r",stdin)
#define fuck(x) cout<<'['<<x<<']'<<endl
using namespace std;
typedef long long LL;
typedef pair<int,int>pii;
const int MX = 1e4 + 5;
const int maxn = 5e4 + 10;
struct edge{
int v,w;
int nxt;
}E[maxn<<1];
int head[MX],tot;//邻接表建图;
int ss[1005];
bool visit[MX];//记录当前这个城市是否为特殊城市;
int n,m,s,q;
void init(){
memset(visit,false,sizeof(visit));
memset(head,-1,sizeof(head));
tot = 0;
}
void add_edge(int u,int v,int w){
E[tot].v = v;
E[tot].w = w;
E[tot].nxt = head[u];
head[u] = tot++;
}
int dis[MX];
bool vis[MX];
struct node{
int x,w;
bool operator <(const node &node1)const{
return w > node1.w;
}
};
bitset<1005>ans[MX];//bitset维护答案;
void dijkstra(int st){
memset(dis,0x3f,sizeof(dis));
memset(vis,false,sizeof(vis));
dis[st] = 0;
priority_queue<node>que;
que.push(node{st,0});
while(!que.empty()){
node nw = que.top();que.pop();
int u = nw.x;
if(vis[u]) continue;
vis[u] = true;
for(int i = head[u];~i;i=E[i].nxt){
int v = E[i].v;
if(dis[v] > nw.w + E[i].w){
dis[v] = nw.w + E[i].w;
que.push(node{v,dis[v]});
if(visit[v]) continue;//如果自身就是特殊城市,那么就不用再次更新了;
ans[v].reset();
for(int i = 0;i != s;i++){
if(ans[u].test(i))
ans[v].set(i);//将离前一个城市最近的几个特殊城市记录到当前点;
}
} else if(dis[v] == nw.w + E[i].w){//相等时说明有多个可能性,将所有可能都记录下来;
for(int i = 0;i != s;i++){
if(ans[u].test(i))
ans[v].set(i);
}
}
}
}
}
int Ans[1005];
int main(){
//FIN;
while(~scanf("%d%d%d%d",&n,&m,&s,&q)){
init();
for(int i = 0;i <= n;i++) ans[i].reset();
for(int i = 0;i < m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add_edge(u,v,w);add_edge(v,u,w);
}
int st = 0;
for(int i = 0;i < s;i++){
int S;
scanf("%d",&ss[i]);
visit[ss[i]] = true;
add_edge(st,ss[i],0);//从超级源点向特殊城市连边;
ans[ss[i]].set(i);//特殊城市的答案就是它本身;
}
dijkstra(st);//dijkstra跑一遍,预处理出答案;
while(q--){
int x;
scanf("%d",&x);
int cnt = 0;
for(int i = 0;i != s;i++){
if(ans[x].test(i))
Ans[cnt++] = ss[i];
}
sort(Ans,Ans+cnt);
for(int i = 0;i < cnt;i++)
printf("%d%c",Ans[i],i == cnt - 1 ? '\n' : ' ');
}
}
return 0;
}