1. 程式人生 > >2005 Nearest Maintenance Point (最短路+bitset維護)

2005 Nearest Maintenance Point (最短路+bitset維護)

題目大意:一個國家裡有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;
}