Problem:
JOI 国のお祭り事情(Festivals in JOI Kingdom)

JOI 国にはN 個の街があり，それらの間はM 本の双方向に通行可能な道路で結ばれている．国民は道路を通ってそれらの街を移動する．

JOI 国の国民には，お祭りが好きな人が多く，現在，K 個の街でお祭りが開催されており，非常に賑わっている．一方で，一部の国民はお祭りを騒がしいとして嫌い，お祭りにできるだけ近づきたくないと思っている．

そこで国王は，優秀なプログラマーであるあなたに，そのようなお祭りを嫌う国民のため，ある街からある街に移動するために，お祭りが開催されている街にどれだけ近づかずに移動することができるかを高速に調べることのできるプログラムの作成を依頼した．

課題

道路の情報とお祭りが開催されている街の情報，およびQ 個のクエリ（出発する街Si と行きたい街Tiの組）が与えられる．各クエリi に対し，街Si から街Ti へのすべての経路のうち，お祭りまでの距離が最大となる経路の，お祭りまでの距離を求めるプログラムを作成せよ．ただし，ある経路のお祭りまでの距離とは，経路上の街からお祭りが開催されている街までの移動距離の最小値のことである．

制限

2 ≤ N ≤ 100000 (= 105)     JOI 国の街の個数

1 ≤ M ≤ 200000 (= 2 × 105)     JOI 国の道路の本数

1 ≤ K ≤ N     お祭りが開催されている街の個数

1 ≤ Q ≤ 100000 (= 105)     クエリの個数

1 ≤ Li ≤ 1000     i 番目の道路の長さ

入力

標準入力から以下のデータを読み込め．

1 行目には整数N, M, K, Q が空白を区切りとして書かれている．N はJOI 国の街の個数を，M はJOI国の道路の本数を，K はお祭りが開催されている街の個数を，Q はクエリの個数をそれぞれ表す．街には1, 2, ... , N の番号がつけられている．

続くM 行は道路の情報を表す．i + 1 行目(1 ≤ i ≤ M) には整数Ai, Bi, Li (1 ≤ Ai ≤ N, 1 ≤ Bi ≤ N) が空白を区切りとして書かれている．これは，i 番目の道路が街Ai と街Bi を結んでおり，長さがLi であることを表す．道路の両端が同じ街であることはない．また，任意の2 つの街p, q に対し，p とqを結ぶ道路は2 本以上存在しない．どの街からどの街へもいくつかの道路をたどって行くことができる．

続くK 行はお祭りが開催されている街の情報を表す．i + M + 1 行目(1 ≤ i ≤ K) には1 つの整数Fi(1 ≤ Fi ≤ N) が書かれている．これは街Fi でお祭りが開催されていることを表す．F1, ..., FK の中に同じ値が2 回以上現れることはない．

続くQ 行はクエリを表す．i + M + K + 1 行目(1 ≤ i ≤ Q) には2 つの整数Si, Ti (1 ≤ Si ≤ N, 1 ≤ Ti ≤ N, Si ≠ Ti) が空白を区切りとして書かれている．これはi 番目のクエリの出発する街がSi であり行きたい街がTi であることを表す．

出力

標準出力に，全クエリへの答えをQ 行で出力せよ．すなわち，i 行目に，街Si から街Ti へのすべての経路のうち，お祭りまでの距離が最大となる経路の，お祭りまでの距離を表す整数を出力せよ．

採点基準

採点用データのうち，

配点の10% 分については，Q = 1 を満たす．

配点の20% 分については，N ≤ 5000, Q ≤ 5000 を満たす．

配点の30% 分については，これら2 つの条件の少なくとも一方を満たす．また，これら2 つの条件の両方を満たすような採点用データはない．

入出力例

入力例 1

6 6 2 3
1 2 5
2 3 4
2 4 6
3 5 9
4 5 3
5 6 7
1
6
3 4
5 2
1 4

出力例 1

7
5
0

6 つの街が6 本の道路で結ばれており，お祭りは街1, 6 の2 つの街で開催されている．クエリは以下の3 つである．

1 つ目のクエリは街3 から街4 へ行くというものである．街2 を経由する経路ではお祭りまでの距離は5，街5 を経由する経路ではお祭りまでの距離は7 となるため，答えは7 である．

2 つ目のクエリは街5 から街2 へ行くというものである．街3 と街4 のどちらを経由しても，街2 でお祭りまでの距離が最小となり，答えは5 である．

3 つ目のクエリは街1 から街4 へ行くというものである．街1 はお祭りが開催されている街であるため，答えは0 となる．

図1: 入力例1

入力例 2

12 17 2 5
1 3 6
1 6 7
2 3 8
2 4 4
2 8 11
2 12 2
3 6 3
3 7 8
3 11 2
4 12 2
5 10 3
6 10 5
8 9 6
8 12 7
9 10 6
11 9 10
12 9 5
8
7
2 6
5 2
1 10
8 9
9 4

出力例 2

8
8
11
0
6

図1: 入力例2

問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

Solution in C (judge verdict: Accepted)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define DIJKSTRA_MAX_NODE 100000
#define DIJKSTRA_MAX_EDGE 400000

typedef struct {
	int from,to;
	int cost;
} dijkstra_edge_t;

typedef struct {
	int node;
	int cost;
} dijkstra_node_t;

int dijkstra_edge_num;
dijkstra_edge_t dijkstra_edge[DIJKSTRA_MAX_EDGE];
int dijkstra_node_edge[DIJKSTRA_MAX_NODE][2];
int dijkstra_queue_num;
dijkstra_node_t dijkstra_queue[DIJKSTRA_MAX_EDGE+1];
int dijkstra_cost[DIJKSTRA_MAX_NODE];
char dijkstra_visited[DIJKSTRA_MAX_NODE];

int dijkstra_comp(const void* x,const void* y) {
	const dijkstra_edge_t* a=(const dijkstra_edge_t*)x;
	const dijkstra_edge_t* b=(const dijkstra_edge_t*)y;
	if((a->from)>(b->from))return 1;
	if((a->from)<(b->from))return -1;
	if((a->to)>(b->to))return 1;
	if((a->to)<(b->to))return -1;
	return 0;
}

void dijkstra_queue_adjust(int pos) {
	int min=pos;
	if(pos*2+1<dijkstra_queue_num &&
		dijkstra_queue[min].cost>dijkstra_queue[pos*2+1].cost)
			min=pos*2+1;
	if(pos*2+2<dijkstra_queue_num &&
		dijkstra_queue[min].cost>dijkstra_queue[pos*2+2].cost)
			min=pos*2+2;
	if(min!=pos) {
		dijkstra_node_t temp;
		temp=dijkstra_queue[min];
		dijkstra_queue[min]=dijkstra_queue[pos];
		dijkstra_queue[pos]=temp;
		dijkstra_queue_adjust(min);
	} else if(pos>0)dijkstra_queue_adjust((pos-1)/2);
}

void dijkstra_queue_push(int node,int cost) {
	dijkstra_node_t topush;
	topush.node=node;topush.cost=cost;
	dijkstra_queue[dijkstra_queue_num++]=topush;
	dijkstra_queue_adjust(dijkstra_queue_num-1);
}

dijkstra_node_t dijkstra_queue_pop(void) {
	dijkstra_node_t result=dijkstra_queue[0];
	if(dijkstra_queue_num>0) {
		dijkstra_queue[0]=dijkstra_queue[--dijkstra_queue_num];
		dijkstra_queue_adjust(0);
	}
	return result;
}

void dijkstra_init(void) {
	dijkstra_edge_num=0;
}

void dijkstra_addedge(int from,int to,int cost) {
	dijkstra_edge[dijkstra_edge_num].from=from;
	dijkstra_edge[dijkstra_edge_num].to=to;
	dijkstra_edge[dijkstra_edge_num].cost=cost;
	dijkstra_edge_num++;
}

void dijkstra_setup(void) {
	int i;
	int prev;
	qsort(dijkstra_edge,dijkstra_edge_num,
		sizeof(dijkstra_edge_t),dijkstra_comp);
	prev=0;
	memset(dijkstra_node_edge,0,sizeof(dijkstra_node_edge));
	dijkstra_node_edge[0][0]=0;
	for(i=0;i<dijkstra_edge_num;i++) {
		if(dijkstra_edge[i].from!=dijkstra_edge[prev].from) {
			dijkstra_node_edge[dijkstra_edge[prev].from][1]=i;
			dijkstra_node_edge[dijkstra_edge[i].from][0]=i;
			prev=i;
		}
	}
	dijkstra_node_edge[dijkstra_edge[prev].from][1]=i;
}

void dijkstra_search(const int* from,int from_num) {
	int i;
	memset(dijkstra_cost,0xFF,sizeof(dijkstra_cost));
	memset(dijkstra_visited,0,sizeof(dijkstra_visited));
	dijkstra_queue_num=0;
	for(i=0;i<from_num;i++)dijkstra_queue_push(from[i],0);
	while(dijkstra_queue_num>0) {
		dijkstra_node_t nownode=dijkstra_queue_pop();
		if(!dijkstra_visited[nownode.node]) {
			dijkstra_visited[nownode.node]=1;
			dijkstra_cost[nownode.node]=nownode.cost;
			for(i=dijkstra_node_edge[nownode.node][0];
					i<dijkstra_node_edge[nownode.node][1];i++) {
				if(!dijkstra_visited[dijkstra_edge[i].to]) {
					dijkstra_queue_push(
						dijkstra_edge[i].to,
						nownode.cost+dijkstra_edge[i].cost
					);
				}
			}
		}
	}
}

#define UNION_FIND_MAX 100000

int uf_table[UNION_FIND_MAX];

void uf_init(void) {
	int i;
	for(i=0;i<UNION_FIND_MAX;i++)uf_table[i]=0;
}

void uf_merge(int a,int b) {
	int next;
	while(uf_table[a]>0) {
		next=uf_table[a]-1;
		if(uf_table[next]>0)uf_table[a]=uf_table[next];
		a=next;
	}
	while(uf_table[b]>0) {
		next=uf_table[b]-1;
		if(uf_table[next]>0)uf_table[b]=uf_table[next];
		b=next;
	}
	if(a!=b)uf_table[a]=b+1;
}

int uf_issame(int a,int b) {
	int next;
	while(uf_table[a]>0) {
		next=uf_table[a]-1;
		if(uf_table[next]>0)uf_table[a]=uf_table[next];
		a=next;
	}
	while(uf_table[b]>0) {
		next=uf_table[b]-1;
		if(uf_table[next]>0)uf_table[b]=uf_table[next];
		b=next;
	}
	return a==b;
}

int mati_num;
int douro_num;
int omaturi_num;
int omaturi_list[100000];
int query_num;
dijkstra_edge_t douro[200000];
dijkstra_edge_t douro_use[200000];
int mati_douro[100000][2];
int tree_parent[100000];
int tree_depth[100000];
int lca_goto[18][100000];
int lca_cost[18][100000];

int edge_comp_cost(const void* x,const void* y) {
	const dijkstra_edge_t* a=(const dijkstra_edge_t*)x;
	const dijkstra_edge_t* b=(const dijkstra_edge_t*)y;
	if((a->cost)<(b->cost))return 1;
	if((a->cost)>(b->cost))return -1;
	return 0;
}

void make_tree(int now,int parent,int depth) {
	int i;
	tree_parent[now]=parent;
	tree_depth[now]=depth;
	for(i=mati_douro[now][0];i<mati_douro[now][1];i++) {
		if(douro_use[i].to!=parent) {
			make_tree(douro_use[i].to,now,depth+1);
		}
	}
}

void lca_setup(void) {
	int i,j;
	for(i=0;i<mati_num;i++) {
		lca_goto[0][i]=tree_parent[i];
		lca_cost[0][i]=dijkstra_cost[i];
		if(tree_parent[i]>=0 &&
				dijkstra_cost[tree_parent[i]]<dijkstra_cost[i]) {
			lca_cost[0][i]=dijkstra_cost[tree_parent[i]];
		}
	}
	for(i=1;i<18;i++) {
		for(j=0;j<mati_num;j++) {
			lca_goto[i][j]=lca_goto[i-1][lca_goto[i-1][j]];
			lca_cost[i][j]=lca_cost[i-1][j];
			if(lca_goto[i-1][j]>=0 &&
					lca_cost[i-1][lca_goto[i-1][j]]<lca_cost[i-1][j]) {
				lca_cost[i][j]=lca_cost[i-1][lca_goto[i-1][j]];
			}
		}
	}
}

int lca_go(int* cost,int start,int go) {
	int thecost=dijkstra_cost[start];
	int now=start;
	int i;
	for(i=0;i<18;i++) {
		if(go & (1<<i)) {
			if(lca_cost[i][now]<thecost)thecost=lca_cost[i][now];
			now=lca_goto[i][now];
		}
	}
	if(cost)*cost=thecost;
	return now;
}

int lca_query(int a,int b) {
	int thecost=0x7fffffff;
	int nextcost;
	int left,right,mid;
	if(a==b)return dijkstra_cost[a];
	if(tree_depth[a]>tree_depth[b]) {
		a=lca_go(&thecost,a,tree_depth[a]-tree_depth[b]);
	} else if(tree_depth[a]<tree_depth[b]) {
		b=lca_go(&thecost,b,tree_depth[b]-tree_depth[a]);
	}
	left=0;right=tree_depth[a]-1;
	while(left<=right) {
		mid=(left+right)/2;
		if(lca_go(NULL,a,mid)==lca_go(NULL,b,mid)) {
			right=mid-1;
		} else {
			left=mid+1;
		}
	}
	lca_go(&nextcost,a,right+1);
	if(nextcost<thecost)thecost=nextcost;
	lca_go(&nextcost,b,right+1);
	if(nextcost<thecost)thecost=nextcost;
	return thecost;
}

int main(void) {
	int i;
	int connect_count;
	int prev;
	scanf("%d%d%d%d",&mati_num,&douro_num,&omaturi_num,&query_num);
	/* dijkstra */
	dijkstra_init();
	for(i=0;i<douro_num;i++) {
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		a--;b--;
		dijkstra_addedge(a,b,c);
		dijkstra_addedge(b,a,c);
		douro[i].from=a;
		douro[i].to=b;
	}
	dijkstra_setup();
	for(i=0;i<omaturi_num;i++) {
		scanf("%d",&omaturi_list[i]);
		omaturi_list[i]--;
	}
	dijkstra_search(omaturi_list,omaturi_num);

	/* make tree */
	for(i=0;i<douro_num;i++) {
		if(dijkstra_cost[douro[i].from]<dijkstra_cost[douro[i].to]) {
			douro[i].cost=dijkstra_cost[douro[i].from];
		} else {
			douro[i].cost=dijkstra_cost[douro[i].to];
		}
	}
	qsort(douro,douro_num,sizeof(dijkstra_edge_t),edge_comp_cost);
	uf_init();
	connect_count=0;
	for(i=0;i<douro_num;i++) {
		if(!uf_issame(douro[i].from,douro[i].to)) {
			uf_merge(douro[i].from,douro[i].to);
			douro_use[connect_count*2].from=douro[i].from;
			douro_use[connect_count*2].to=douro[i].to;
			douro_use[connect_count*2].cost=douro[i].cost;
			douro_use[connect_count*2+1].from=douro[i].to;
			douro_use[connect_count*2+1].to=douro[i].from;
			douro_use[connect_count*2+1].cost=douro[i].cost;
			connect_count++;
		}
	}
	connect_count*=2;
	qsort(douro_use,connect_count,
		sizeof(dijkstra_edge_t),dijkstra_comp);
	prev=0;
	memset(mati_douro,0,sizeof(mati_douro));
	mati_douro[0][0]=0;
	for(i=0;i<connect_count;i++) {
		if(douro_use[i].from!=douro_use[prev].from) {
			mati_douro[douro_use[prev].from][1]=i;
			mati_douro[douro_use[i].from][0]=i;
			prev=i;
		}
	}
	mati_douro[douro_use[prev].from][1]=i;

	/* calculate query */
	make_tree(0,-1,1);
	lca_setup();
	for(i=0;i<query_num;i++) {
		int a,b;
		scanf("%d%d",&a,&b);
		a--;b--;
		printf("%d\n",lca_query(a,b));
	}

	return 0;
}