2017년 6월 27일 화요일

(LCA - Lowest Common Ancestor) 1761 정점들의 거리

백준 1761 정점들의 거리 https://www.acmicpc.net/problem/1761
3176 도로 네트워크 https://www.acmicpc.net/problem/3176
Codeforces #425 (Div. 2) D - Misha, Grisha and Underground  
http://codeforces.com/contest/832/problem/D

LCA (최소 공통 조상) 
어떤 두 정점의 가장 가까운 공통 루트가 누구인지 알아내는 문제이다.
종만북에는 세그먼트 트리로 LCA를 구하지만 넘 복잡하고 보통 DP를 이용해서 구한다.

DFS로 노드들의 depth를 구한 후 $2^K$위의 루트 노드가 무엇인지 DP를 이용하여 전처리한다.
여기서 정점사이의 거리나 최소거리 등의 정보도 함께 넣을 수 있다.

1. 정점들의 거리
트리에서 두 정점 사이의 거리를 구하는 문제이다. 
전처리 할 때 현재 노드에서 $2^K$위의 루트 노드 까지 거리도 전처리 해준다.
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
#define INF 987654321
int N, M;
vector<pair<intint>> adj[40001];
bool visit[40001];
int depth[40001], par[40001][17], dist[40001][17];
void dfs(int here, int d){
    visit[here] = true;
    depth[here] = d;
    for (auto n : adj[here]){
        int next = n.second;
        int cost = n.first;
        if (!visit[next]){
            par[next][0= here;
            dist[next][0= cost;
            dfs(next, d + 1);
        }
    }
}
void dp(){
    for (int m = 1; m < 17; m++){
        for (int n = 1; n <= N; n++){
            par[n][m] = par[par[n][m - 1]][m - 1];
            dist[n][m] = dist[n][m - 1+ dist[par[n][m - 1]][m - 1];
        }
    }
}
int lca(int u, int v){
    int ret = 0;
    if (depth[u] < depth[v]) swap(u, v);
    for (int i = 16; i >= 0; i--){
        if (depth[u] - depth[v] >= (1 << i)){
            ret += dist[u][i];
            u = par[u][i];
        }
    }
    //depth같아짐
    if (u == v) return ret;
    for (int i = 16; i >= 0; i--){
        if (par[u][i] != par[v][i]){
            ret += (dist[u][i] + dist[v][i]);
            u = par[u][i], v = par[v][i];
        }
    }
    ret += (dist[u][0+ dist[v][0]);
    return ret;
}
int main(){
    scanf("%d"&N);
    for (int n = 0; n < N - 1; n++){
        int u, v, d;
        scanf("%d%d%d"&u, &v, &d);
        adj[u].push_back({ d, v });
        adj[v].push_back({ d, u });
    }
    //전처리
    dfs(10);
    dp();
    scanf("%d"&M);
    while (M--){
        int a, b;
        scanf("%d%d"&a, &b);
        printf("%d\n", lca(a, b));
    }
    return 0;
}
cs

2. 도로 네트워크
문제를 잘 읽어보면 네트워크는 트리형태이다.
이 문제에서는 최소 간선, 최대 간선에 대해 전처리 해주면 된다.
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
#define INF 987654321
int N, K;
vector<pair<intint>> adj[100001];
bool visit[100001];
int depth[100001];
int par[100001][21];
int edge_min[100001][21], edge_max[100001][21];
void dfs(int here, int d){
    visit[here] = true;
    depth[here] = d;
    for (auto n : adj[here]){
        int next = n.second;
        int cost = n.first;
        if (!visit[next]){
            par[next][0= here;
            edge_min[next][0= cost;
            edge_max[next][0= cost;
            dfs(next, d + 1);
        }
    }
}
void dp(){
    for (int n = 1; n < 21; n++){
        for (int m = 1; m <= N; m++){
            par[m][n] = par[par[m][n - 1]][n - 1];
            edge_min[m][n] = min(edge_min[m][n - 1], edge_min[par[m][n - 1]][n - 1]);
            edge_max[m][n] = max(edge_max[m][n - 1], edge_max[par[m][n - 1]][n - 1]);
        }
    }
}
pair<int,int> lca(int a, int b){
    int rmin = INF, rmax = -INF;
    if (depth[a] < depth[b]) swap(a, b);
    for (int i = 20; i >= 0; i--){
        if (depth[a] - depth[b] >= (1 << i)){
            rmin = min(rmin, edge_min[a][i]);
            rmax = max(rmax, edge_max[a][i]);
            a = par[a][i];
        }
    }
    if (a == b) return { rmin, rmax };
    for (int i = 20; i >= 0; i--){
        if (par[a][i] != par[b][i]){
            rmin = min(rmin, min(edge_min[a][i], edge_min[b][i]));
            rmax = max(rmax, max(edge_max[a][i], edge_max[b][i]));
            a = par[a][i];
            b = par[b][i];
        }
    }
    rmin = min(rmin, min(edge_min[a][0], edge_min[b][0]));
    rmax = max(rmax, max(edge_max[a][0], edge_max[b][0]));
    return { rmin, rmax };
}
int main(){
    scanf("%d"&N);
    for (int n = 0; n < N - 1; n++){
        int u, v, d;
        scanf("%d%d%d"&u, &v, &d);
        adj[u].push_back({ d, v });
        adj[v].push_back({ d, u });
    }
    //전처리
    dfs(10);
    dp();
    scanf("%d"&K);
    for (int k = 0; k < K; k++){
        int D, E;
        scanf("%d%d"&D, &E);
        pair<int,int> ans = lca(D, E);
        printf("%d %d\n", ans.first, ans.second);
    }
    return 0;
}
cs

3. Codeforces #425 (Div. 2) - D
나의 첫 codeforces 대회였다. 
2시간이라는 촉박한 시간과 영어여서 문제이해하는데도 한참 걸렸다.
(그리고 Div. 2 치고 어렵게 나온거같다;; 1문제 품)

B는 문자열, C는 수식과 실수가 나와서 바로넘어갔었다.
D를 실제로 C보다 더 많이 풀었는데 대회때는 못풀었지만 그나마 풀만했던거같다.

트리가 존재한다. Misha는 $s$->$f$를 가며 정점에 낙서를 하고 Grisha는 $f$->$t$를 이동하며 
낙서된 정점의 수를 세는데 Grisha가 세는 정점의 수의 최댓값을 구하는 문제이다.

각 쿼리에는 $s$, $f$, $t$의 후보 $a$, $b$, $c$가 주어진다.

대회때는 2820 자동차 공장풀듯이 세그먼트트리와 lazy propogation 그리고 LCA를 이용해서 풀려고 했지만 끝나고서야 오류를 알았다.

사실은 LCA만으로 풀리는 문제이다.
1. LCA($s$, $f$) == LCA($f$, $t$) == $f$인 경우
2. LCA($s$, $f$) == LCA($f$, $t$) != $f$인 경우
3. LCA($s$, $f$) != LCA($f$, $t$)인 경우에 대해 따져주면 된다.

쿼리에서 입력으로 들어오는 후보가 3개밖에없으니 $3!$으로 모든 경우에 대해 돌려주어 최댓값을 구하면 된다.
int solve(int s, int f, int t) {
    int ret = 0;
    int sf_lca = getLCA(s, f), ft_lca = getLCA(f, t);
    if (sf_lca == ft_lca && sf_lca == f) ret = depth[getLCA(s, t)] - depth[f];
    else if (sf_lca == ft_lca) {
        int common_lca = sf_lca;
        ret = depth[getLCA(s, t)] - depth[common_lca] + depth[f] - depth[common_lca];
    }
    else{
        if (depth[sf_lca] > depth[ft_lca]) ret = depth[f] - depth[sf_lca];
        else ret = 0;
    }
    return ret + 1;
}
cs

3948 홍준이의 친위대

백준 3948 홍준이의 친위대 https://www.acmicpc.net/problem/3948

<유사문제>
1146 지그재그 서기 https://www.acmicpc.net/problem/1146


  1. 맨 앞줄에는 아무나 설 수 있다.
  2. 둘째 줄에도 아무나 설 수 있다.
  3. 셋째 줄에는 둘째 줄에 서 있는 사람이 첫째 줄에 서 있는 사람보다 클 경우, 둘째 줄에 서 있는 사람보다 작은 사람만이 설 수 있으며, 둘째 줄에 서 있는 사람이 첫째 줄에 서 있는 사람보다 작을 경우, 둘째 줄에 서 있는 사람보다 큰 사람만이 설 수 있다.
  4. 넷째 줄부터는 둘째 줄과 셋째 줄을 비교하는 식으로 해서 N번째의 줄을 서는 사람은 N-2번째 줄과 N-1번째 줄에 서는 사람을 비교해서 세운다.
N명의 학생이 존재할 때 다음의 규칙을 따른다. 사실 이 말은 지그재그로 학생을 배치하는 것이다.

문제는 학생의 위치를 저장할 때 bitmask dp나 방문배열로 확인할 수 가 없다. (N이 100이하이므로)

그래서 다른 방법으로 접근해야하는데 바로 현재상태에서 작은사람과 큰사람의 수를 저장하며 비교하는것이다.
#include <cstdio>
#include <cstring>
#define mod 1000000
int N;
int dp[111][111][2];
// 현재 사람보다 작은사람이 S, 큰사람이 L명 있을 때 
// flag(1 : 더 큰사람을 골라야하는 경우, 0 : 더 작은 사람을 골라야하는 경우)
int solve(int L, int S, int flag){        
    int range = L + S;
    if (range == 0return 1;
    
    int &ret = dp[L][S][flag];
    if (ret != -1return ret;
    ret = 0;
    if (!flag)        //S을 골라야함
        for (int i = 0; i < S; i++)
            ret += solve(range - (i + 1), i, 1), ret %= mod;
    else
        for (int i = 0; i < L; i++)
            ret += solve(i, range - (i + 1), 0), ret %= mod;
    return ret % mod;
}
int main(){
    memset(dp, -1sizeof(dp));
    scanf("%d"&N);
    if (N == 1){
        printf("1\n");
        return 0;
    }
    int ans = 0;
    for (int n = 0; n < N; n++){
        ans += solve(n, N - 1 - n, 0);
        ans %= mod;
        ans += solve(n, N - 1 - n, 1);
        ans %= mod;
    }
    printf("%d\n", ans);
    return 0;
}
cs

2017년 6월 22일 목요일

(Dijkstra - 좋은문제) codeground 두 개의 네비게이션

Codeground 두 개의 네비게이션 https://www.codeground.org/practice/practiceProblemView
11569 신호등 https://www.acmicpc.net/problem/11569
13308 주유소 https://www.acmicpc.net/problem/13308
1162 도로포장 https://www.acmicpc.net/problem/1162
1854 K번째 최단경로 찾기 https://www.acmicpc.net/problem/1854
13907 세금 https://www.acmicpc.net/problem/13907

1. 두 개의 네비게이션
두 종류의 네비게이션이 있는데 현재 교차로에서 회사(1)까지 가는 최단경로가 아닌 경로인 경우 
경보음이 울린다.
동현이가 회사(1)에서 집(N)으로 이동하는 경로중 가장 적은 횟수로 경보음이 울리는 경우의 횟수를 
구하는 문제이다. 

android, iphone인경우에 대해 dijkstra알고리즘으로 N에서 1까지 이동하면서 dist를 구해 놓는다.

$dist[here]$ != $dist[next]$ + $d[next][here]$인 지점은 현재 교차로에서 회사까지 가는 최단경로가 
아닌지점으로 경보음을 하나씩 증가 시키며 Heap을 돌린다.


#include <cstdio>
#include <vector>
#include <algorithm>
#include <queue>
#include <functional>
using namespace std;
#define mp(a,b) make_pair(a,b)
#define mt(a,b,c) mp(mp(a,b),c)
#define INF 987654321
typedef pair<intint> P;
typedef pair<pair<intint>int> T;
int N, M;
vector<vector<T> > adj,rev_adj;
vector<int> and_dist, iph_dist;
void dijkstra(int start, const vector<vector<T>> &path, vector<int> &dist, bool flag){    
//if(flag) : android, else : iphone 
    priority_queue<P, vector<P>, greater<P> > pq;
    dist[start] = 0;
    pq.push(mp(0, start));
    while (!pq.empty()){
        int here = pq.top().second;
        pq.pop();
        for (auto n : path[here]){
            int next = n.second;
            int ncost;
            if (flag) ncost = n.first.first + dist[here];
            else ncost = n.first.second + dist[here];
            if (dist[next] > ncost){
                dist[next] = ncost;
                pq.push(mp(ncost, next));
            }
        }
    }
}
int solve(int start){
    priority_queue<P, vector<P>, greater<P> > pq;
    vector<int> bell(N + 1, INF);
    
    bell[start] = 0;
    pq.push(mp(0, start));
    while (!pq.empty()){
        int here = pq.top().second;
        if (here == N) return (pq.top().first);
        pq.pop();
        
        for (auto n : adj[here]){
            int next = n.second;
            int plus = 0;
            if (and_dist[here] != and_dist[next] + n.first.first)
                plus++;
            if (iph_dist[here] != iph_dist[next] + n.first.second)
                plus++;
            if (bell[next] > bell[here] + plus){
                bell[next] = bell[here] + plus;
                pq.push(mp(bell[here] + plus, next));
            }
        }
    }
    return -1;
}
int main(){
    setbuf(stdout, NULL);
    int Test;
    scanf("%d"&Test);
    for (int t = 1; t <= Test; t++){
        scanf("%d%d"&N, &M);
        adj = vector<vector<T>>(N + 1vector<T>());
        rev_adj = vector<vector<T>>(N + 1vector<T>());
        and_dist = vector<int>(N + 1, INF);
        iph_dist = vector<int>(N + 1, INF);
        for (int m = 0; m < M; m++){
            int u, v, A, I;
            scanf("%d%d%d%d"&u, &v, &A, &I);
            adj[u].push_back(mt(A, I, v));
            rev_adj[v].push_back(mt(A, I, u));
        }
        dijkstra(N, rev_adj, and_dist, true);
        dijkstra(N, rev_adj, iph_dist, false);
        printf("#testcase%d\n", t);
        printf("%d\n",solve(1));
    }
    return 0;
}
cs


2. 신호등
신호가 존재하는 경로들이 있을 때 목적지까지 도달하는데 최단시간을 구하는 문제이다.

신호를 잘 처리해주고 par[]배열로 이전 경로를 저장하는 대신 queue에 (from,to)를 넣어 
이전경로를 대체해줘야 맞는 답이 나온다.



#include <cstdio>
#include <queue>
#include <vector>
#include <functional>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef pair<ll, pair<int,int> > P;        
#define INF 987654321987654321
#define mp(a,b) make_pair(a,b)
#define mt(a,b,c) mp(a,mp(b,c))
int N, M, S, D;
vector<vector<pair<int,ll> > > adj;            //정렬 되있음        next, cost
vector<int> delay;                //교차로에서의 주기
ll dijkstra(int start, int end){
    priority_queue<P, vector<P>, greater<P>> pq;        //cost, from, here
    vector<ll> dist(N + 1, INF);
    dist[start] = 0;
    //처음 start->ver 연결된 지점은 교차신호 상관없음
    for (auto n : adj[start]){
        pq.push(mt(n.second, start, n.first));
        dist[n.first] = n.second;
    }
    while (!pq.empty()){
        int here = pq.top().second.second;
        int from = pq.top().second.first;
        ll cost = pq.top().first;
        pq.pop();
        if (here == endreturn dist[end];
        for (auto n : adj[here]){
            int next = n.first;
            ll ncost = n.second;
            
            //주기는 here교차로의 size에 의존함
            //from -> here -> next ,from이 here교차로의 몇번째 order인지 알아야함
            ll T = cost;            //시간
            ll P = delay[here];        //delay
            ll size = adj[here].size();
            int order;            //가고자하는 패턴번호
            for (order = 0; order < adj[here].size(); order++)        //가고자하는 패턴번호를 구함
                if (adj[here][order].first == from) break;
            
            //최종 T를 구함
            int curr = (T / P) % size;        //현재 패턴번호
            
            if (order != curr)
                T = (T / P + (order + size - curr) % size* P;            //가장 근접한 T로 만듬
            if (dist[next] > T + ncost){
                dist[next] = T + ncost;
                pq.push(mt(dist[next], here, next));
            }
        }
    }
    return dist[end];
}
int main(){
    int T;
    scanf("%d"&T);
    while (T--){
        scanf("%d%d%d%d"&N, &M, &S, &D);
        
        adj = vector<vector<pair<int, ll> > >(N + 1);
        delay = vector<int>(N + 1);
        for (int m = 0; m < M; m++){
            int u, v, d;
            scanf("%d%d%d"&u, &v, &d);
            adj[u].push_back(mp(v, (ll)d));
            adj[v].push_back(mp(u, (ll)d));
        }
        for (int n = 1; n <= N; n++){
            scanf("%d"&delay[n]);
            sort(adj[n].begin(), adj[n].end());
        }
        ll ans = dijkstra(S, D);
        printf("%lld\n", ans == INF ? -1 : ans);
    }
    return 0;
}


3. 주유소

이 문제를 풀기전에 13305 주유소 https://www.acmicpc.net/problem/13305를 먼저 풀어본다.

해결방법은 다음에 갈 주유소의 가격이 더 작아질때 까지는 기존의 가격으로 구입하고
아닐 때는 갱신해서 구입하면서 greedy하게 나가면 된다.

이 문제도 위와 같은 방식으로 하되 그래프의 형태이기 때문에 DP를 이용한다.

<13308 주유소 의식의 흐름>
1. 단순히 큐에 dist변수 하나만 넣고 돌려서 "메모리 초과" 발생
2. visit배열을 이용해 방문여부를 판단하며 돌리려 했으나 불가능함을 인식
3. 갱신될때마다 dist배열을 초기화후 돌려서 "시간 초과" 발생 
4. 더이상 생각 못하다 결국 힌트를 얻음 -> dp형식으로 
   dist[N][std] : N까지 오는데 dist[N][std]원 소모, 기름당 가격 : std

#include <cstdio>
#include <vector>
#include <algorithm>
#include <queue>
#include <functional>
using namespace std;
#define INF 987654321
typedef pair<intint> P;
typedef pair<long long, P> TP;
int N, M;
int cost[2501];
vector<pair<intint>> adj[2501];
long long solve(int start, int end) {
    vector<vector<long long>> dist(2502vector<long long>(2502, INF));
    priority_queue<TP, vector<TP>, greater<TP>> pq;
    pq.push({ 0,{ cost[start],start } });        // dist, standard, next
    while (!pq.empty()) {
        int here = pq.top().second.second;
        int std = pq.top().second.first;
        long long d = pq.top().first;
        pq.pop();
        if (here == endreturn d;
        if (dist[here][std!= INF) continue;
        dist[here][std= d;
        for (auto n : adj[here]) {
            int next = n.second;
            long long ndist = d + std*n.first;
            if (dist[next][min(std, cost[next])] == INF) {
                pq.push({ ndist,{ min(std,cost[next]), next } });
            }
        }
    }
    return -1;
}
int main() {
    scanf("%d%d"&N, &M);
    for (int n = 1;n <= N;n++scanf("%d"&cost[n]);
    for (int m = 0;m < M;m++) {
        int u, v, d;
        scanf("%d%d%d"&u, &v, &d);
        adj[u].push_back({ d,v });
        adj[v].push_back({ d,u });
    }
    printf("%lld\n", solve(1, N));
    return 0;
}
cs

4. 도로포장
1번정점에서 N번정점으로 이동하는데 최소시간을 구하는데 최대 K개의 간선의 
cost를 0으로 처리할 수 있다.

Dijkstra DP로 풀 수 있다. 
queue에 이전에 온 정점을 넣어서 왕복처리되지 않게 최적화 시켰는데 별 상관없다.

#include <cstdio>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
#define INF 987654321987654321
int N, M, K;
vector<pair<long longint>> adj[10001];
long long dist[10001][21];
long long dijkstra(int start, int end) {
    long long ret = INF;
    priority_queue<pair<pair<long longint>,pair<int,int>>> pq;        //dist, K, here, prev
    for (int n = 2;n <= N;n++)
        for (int k = 0;k <= K;k++)
            dist[n][k] = INF;
    pq.push({ {0,0},{start,-1} });
    while (!pq.empty()) {
        int here = pq.top().second.first;
        int prev = pq.top().second.second;
        int hK = pq.top().first.second;
        long long cost = -pq.top().first.first;
        pq.pop();
        if (here == end) {
            ret = min(ret, dist[here][hK]);
            continue;
        }
        for (auto n : adj[here]) {
            int next = n.second;
            long long ncost = n.first + cost;
            if (dist[next][hK] > ncost) {
                dist[next][hK] = ncost;
                pq.push({ {-ncost,hK}, {next,here} });
            }
            if (dist[next][hK + 1> cost && hK + 1 <= K && next != prev) {
                dist[next][hK + 1= cost;
                pq.push({ { -cost,hK + 1 },{next, here} });
            }
        }
    }
    return ret;
}
int main() {
    scanf("%d%d%d"&N, &M, &K);
    for (int m = 0;m < M;m++) {
        int u, v, d;
        scanf("%d%d%d"&u, &v, &d);
        adj[u].push_back({ d,v });
        adj[v].push_back({ d,u });
    }
    long long ans = dijkstra(1, N);
    printf("%lld\n", ans);
    return 0;
}
cs

5. K번째 최단경로 찾기
1번 도시에서 다른 도시들로 갈때 K번째 최단경로를 찾는 문제이다.

처음에는 정점을 K번째 까지 오는것만으로 조건을 주어서 풀었는데 시간이 오래걸렸다.
정점에 돌아온 정보를 Max Heap에 저장시켜서 갱신시켜주면 된다.
#include <cstdio>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
#define MAX 2147483647
int N, M, K;
vector<pair<int,pair<intint>>> adj[1001];
priority_queue<pair<intint>> pq;
void dijkstra() {
    priority_queue<int> dist[1001];
    dist[1].push(0);
    pq.push({ 0,1 });
    while (!pq.empty()) {
        int here = pq.top().second;
        int cost = -pq.top().first;
        pq.pop();
        
        for (auto n : adj[here]) {
            int next = n.second.second;
            int ncost = n.second.first + cost;
            if (dist[next].size() < K) {
                dist[next].push(ncost);
                pq.push({ -ncost,next });
            }
            else {
                if (dist[next].top() > ncost) {
                    dist[next].pop();
                    dist[next].push(ncost);
                    pq.push({ -ncost,next });
                }
            }
        }
    }
    for (int n = 1;n <= N;n++) {
        if (dist[n].size() < K) printf("-1\n");
        else printf("%d\n", dist[n].top());
    }
}
int main() {
    scanf("%d%d%d"&N, &M, &K);
    for (int m = 0;m < M;m++) {
        int u, v, d;
        scanf("%d%d%d"&u, &v, &d);
        adj[u].push_back({ m,{ d,v } });
    }
    dijkstra();
    return 0;
}
cs

6. 세금
각 도로마다 통행료가 있다.
세금이 오르면 오른 세금만큼 모든 도로의 통행료가 오른다.
K번 세금이 오를때 S에서 D로 이동하는 최소통행료를 구하는 문제다.

K번마다 오른 세금으로 dijkstra를 돌리면 당연히 TLE가 나온다.
dp형식으로 S에서 도달하는 정점V에 대해 거쳐온 도로 수도 저장해준다.
정점이 N개있으니 어떠한 정점V로 가는데 거쳐온 도로 수도 최대 N개가 된다.
#include <cstdio>
#include <vector>
#include <queue>
#include <algorithm>
#include <functional>
using namespace std;
#define INF 987654321987654321
typedef long long ll;
typedef pair<pair<ll,int>int> P;        //dist, edge cnt, vertex
#define mp(a,b) make_pair(a,b)
#define mt(a,b,c) mp(mp(a,b),c)
int N, M, K;
int S, E;
vector<pair<intint>> adj[1001];
ll dist[1001][1001];
void dijkstra(ll &first) {
    for (int n = 1;n <= N;n++)
        for (int m = 0;m < 1001;m++) dist[n][m] = INF;
    priority_queue<P, vector<P>, greater<P>> pq;
    dist[S][0= 0;
    pq.push(mt(00, S));
    while (!pq.empty()) {
        int here = pq.top().second;
        int cnt = pq.top().first.second;
        ll cost = pq.top().first.first;
        pq.pop();
        bool find = false;
        for (int i = 0;i < cnt;i++) {
            if (dist[here][i] < cost) {
                find = true;
                break;
            }
        }
        if (find || dist[here][cnt] < cost) continue;
        if (here == E) {
            first = min(first, dist[here][cnt]);
            continue;
        }
        for (auto n : adj[here]) {
            int next = n.second;
            ll ncost = (ll)n.first;
            if (cnt + 1 <= N && dist[next][cnt + 1> dist[here][cnt] + ncost) {
                dist[next][cnt + 1= dist[here][cnt] + ncost;
                pq.push(mt(dist[next][cnt + 1], cnt + 1, next));
            }
        }
    }
}
int main() {
    scanf("%d%d%d"&N, &M, &K);
    scanf("%d%d"&S, &E);
    for (int m = 0;m < M;m++) {
        int u, v, d;
        scanf("%d%d%d"&u, &v, &d);
        adj[u].push_back({ d,v });
        adj[v].push_back({ d,u });
    }
    ll first = INF;
    dijkstra(first);
    printf("%lld\n", first);
    vector<pair<ll,ll>> re;
    for (int e = 1;e <= N;e++) {
        if (dist[E][e] != INF) {
            re.push_back(mp(dist[E][e], (ll)e));
        }
    }
    ll sum_tax = 0, tax;
    for (int k = 0;k < K;k++) {
        scanf("%lld"&tax);
        sum_tax += tax;
        ll ret = INF;
        for (auto n : re) {
            ret = min(ret, n.first + n.second*sum_tax);
        }
        printf("%lld\n", ret);
    }
    return 0;
}
cs