Problem:
MathJax.Hub.Config({
tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]}
});

F: カードゲーム

問題

カードを使ったゲームを $Q$ 回行います。
カードには $1 \cdots N$ の数が書かれており、各数が書かれたカードはゲームを行うのに十分な枚数があります。
$i$ 回目のゲームでは、はじめに手札として 2 枚のカードが配られます。
それぞれのカードに書かれている数字は $x_i$ と $y_i$ です。
カードはルールに従って交換することができます。
$j$ 番目 $(1 \le j \le M)$ のルールでは、カード $a_j$ を手数料 $c_j$ 円で別のカード $b_j$ に交換することができます。
各ルールは何回でも用いることができます。また、手数料が足りなくて交換できない場合はありません。
最後に、手札のカードの数字を $R$ で割ったあまりが等しい時、報酬として $z_i$ 円受け取ります。
異なる場合報酬は $0$ 円です。

$Q$ 回ゲームを終えた時に増やすことのできるお金の最大値を求めてください。

制約

$1 \le N \le 10^5$

$0 \le M \le \min(2 \times 10^5, N\times(N-1))$

$2 \le R \le 10$

$1 \le Q \le 10^5$

$1 \le a_j, b_j \le N$

$a_j \neq b_j$

$0 \le c_j \le 10^5$

$1 \le x_i, y_i \le N$

$0 \le z_i \le 10^5$

入力

入力は以下の形式で標準入力から与えられます。

$N \ M \ R \ Q$

$a_1 \ b_1 \ c_1$

$\vdots$

$a_M \ b_M \ c_M$

$x_1 \ y_1 \ z_1$

$\vdots$

$x_Q \ y_Q \ z_Q$

出力

ゲームを $Q$ 回行ったときに増やすことのできるお金の最大値を 1 行で出力してください。また、末尾に改行も出力してください。

サンプル

入力例 1

4 4 2 2
1 2 1
2 3 1
3 4 5
4 1 7
1 4 5
2 4 3

出力例 1

7

1 回目のゲームではカード 1 を手数料 1 でカード 2 に交換すると手札のカードを 2 で割った時のあまりがどちらも 0 となり、得られる報酬は 5 となります。
2 回目のゲームではすでに 2 で割ったときのあまりがどちらも 0 であり、得られる報酬は 3 となります。
2 回のゲームの結果、増やせるお金は $5-1+3=7$ です。

入力例 2

4 4 2 1
1 2 1
2 3 1
3 4 5
4 1 7
3 4 2

出力例 2

0

カードの交換での手数料が報酬よりも多いので、交換を行わないのが最適です。

Solution in C# (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;

class Program
{
    static void Main()
    {
        var a = Console.ReadLine().Split().Select(int.Parse).ToArray();
        int n = a[0], m = a[1], r = a[2], q = a[3];
        long inf = (long)1e16;
        var nodes = new node[n + r + 1];
        var edge = new List<Tp>[n + 1];
        for (int i = 0; i <= n; i++)
        {
            edge[i] = new List<Tp>();
        }
        for (int i = 0; i < m; i++)
        {
            a = Console.ReadLine().Split().Select(int.Parse).ToArray();
            edge[a[1]].Add(new Tp(a[0], a[2]));
        }
        for (int i = 0; i <= n; i++)
        {
            nodes[i] = new node(i, 0, edge[i]);
        }
        var edr = new List<Tp>[r];
        for (int i = 0; i < r; i++)
        {
            edr[i] = new List<Tp>();
            for (int j = i; j <= n; j += r)
            {
                edr[i].Add(new Tp(j, 0));
            }
        }
        for (int i = 0; i < r; i++)
        {
            nodes[i + n + 1] = new node(i + n + 1, 0, edr[i]);
        }
        var costs = new long[r][];
        for (int i = 0; i < r; i++)
        {
            costs[i] = dijk(nodes, i + n + 1);
        }
        long sum = 0;
        for (int i = 0; i < q; i++)
        {
            a = Console.ReadLine().Split().Select(int.Parse).ToArray();
            long cost = inf;
            for (int j = 0; j < r; j++)
            {
                cost = Math.Min(cost, costs[j][a[0]] + costs[j][a[1]]);
            }
            if (a[2] > cost)
                sum += a[2] - cost;
        }
        Console.WriteLine(sum);
    }
    public struct Tp
    {
        public int Item1;
        public long Item2;
        public Tp(int a, long b)
        {
            Item1 = a;
            Item2 = b;
        }
    }
    public class node : IComparable<node>
    {
        public int id;
        public long dist;
        public List<Tp> edge;
        public int CompareTo(node x)
        {
            return x.dist.CompareTo(this.dist);
        }
        public node(int id) : this(id, 0, new List<Tp>()) {}
        public node(int id, long dist, List<Tp> edge)
        {
            this.id = id;
            this.dist = dist;
            this.edge = edge;
        }
        public void addedge(int to, long cost)
        {
            edge.Add(new Tp(to, cost));
        }
    }
    public class PQ
    {
        List<node> buf;
        public PQ()
        {
            buf = new List<node>();
        }
        void swap(int i, int j)
        {
            var t = buf[i];
            buf[i] = buf[j];
            buf[j] = t;
        }
        public void push(node e)
        {
            int n = buf.Count;
            buf.Add(e);
            while (n > 0)
            {
                int i = (n - 1) >> 1;
                if (buf[n].CompareTo(buf[i]) > 0)
                    swap(i, n);

                n = i;
            }
        }
        public node pop()
        {
            node ret = buf[0];
            int n = buf.Count - 1;
            buf[0] = buf[n];
            buf.RemoveAt(n);
            for (int i = 0, j; (j = (i << 1) + 1) < n; i = j)
            {
                if (j != n - 1 && buf[j].CompareTo(buf[j + 1]) < 0)
                    ++j;
                if (buf[i].CompareTo(buf[j]) < 0)
                    swap(i, j);
            }
            return ret;
        }
        public int Count {get{return buf.Count;}}
    }
    public static long[] dijk(node[] nodes, int s)
    {
        int n = nodes.Length;
        long[] dist = new long[n];
        var q = new PQ();
        for (int i = 0; i < n; i++)
        {
            dist[i] = (long)1e16;
        }
        dist[s] = 0;
        q.push(nodes[s]);
        while (q.Count > 0)
        {
            var p = q.pop();
            if (p.dist.CompareTo(dist[p.id]) > 0)continue;
            foreach (var i in p.edge)
            {
                var d = dist[p.id] + i.Item2;
                if (dist[i.Item1].CompareTo(d) > 0)
                {
                    dist[i.Item1] = d;
                    q.push(new node(i.Item1, d, nodes[i.Item1].edge));
                }
            }
        }
        return dist;
    }
}