Problem:
Score : 400 points

Problem Statement

There is a directed graph with N vertices and M edges.
The i-th edge (1≤i≤M) points from vertex a_i to vertex b_i, and has a weight c_i.
We will play the following single-player game using this graph and a piece.

Initially, the piece is placed at vertex 1, and the score of the player is set to 0.
The player can move the piece as follows:

When the piece is placed at vertex a_i, move the piece along the i-th edge to vertex b_i. After this move, the score of the player is increased by c_i.

The player can end the game only when the piece is placed at vertex N.
The given graph guarantees that it is possible to traverse from vertex 1 to vertex N.

When the player acts optimally to maximize the score at the end of the game, what will the score be?
If it is possible to increase the score indefinitely, print inf.

Constraints

2≤N≤1000

1≤M≤min(N(N-1),2000)

1≤a_i,b_i≤N (1≤i≤M)

a_i≠b_i (1≤i≤M)

a_i≠a_j or b_i≠b_j (1≤i<j≤M)

-10^9≤c_i≤10^9 (1≤i≤M)

c_i is an integer.

In the given graph, there exists a path from vertex 1 to vertex N.

Input

Input is given from Standard Input in the following format:

N M
a_1 b_1 c_1
a_2 b_2 c_2
:
a_M b_M c_M

Output

Print the maximum possible score at the end of the game, if it is finite. If it is possible to increase the score indefinitely, print inf.

Sample Input 1

3 3
1 2 4
2 3 3
1 3 5

Sample Output 1

7

There are two ways to move the piece to vertex N=3:

vertex 1 → vertex 2 → vertex 3 : score 4+3=7

vertex 1 → vertex 3 : score 5

Thus, the maximum possible score at the end of the game is 7.

Sample Input 2

2 2
1 2 1
2 1 1

Sample Output 2

inf

It is possible to increase the score indefinitely by alternating between vertex 1 and 2.

Sample Input 3

6 5
1 2 -1000000000
2 3 -1000000000
3 4 -1000000000
4 5 -1000000000
5 6 -1000000000

Sample Output 3

-5000000000

Solution in C# (Mono 4.6.2.0) (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ContestLibraryD061;

namespace ABC_D
{
    class D061
    {
        static void Main(string[] args)
        {
            int[] h = Console.ReadLine().Split(' ').Select(z => int.Parse(z)).ToArray();
            int N = h[0], M = h[1];
            Graph g = new Graph(N, true);
            for(int i = 0; i < M; i++)
            {
                int[] edge = Console.ReadLine().Split(' ').Select(z => int.Parse(z)).ToArray();
                g.AddEdge(edge[0], edge[1], -edge[2]);
            }
            double[] d;
            bool ok=g.BellmanFord(1, out d);
            string r = ok ? (-d[N]).ToString() : "inf";
            Console.WriteLine(r);
        }

    }
}

namespace ContestLibraryD061
{

    class Graph
    {
        public bool UseMatrix { get; set; }
        private bool idZeroStart;
        public List<Vertex> Vertices { get; }
        public List<Edge> Edges { get; }
        public double[,] Matrix { get; private set; }
        public bool IsDirectedGraph { get; }

        public Graph(int N, bool isDirectedGraph = false, bool idZeroStart = false, bool useMatrix = false)
        {
            IsDirectedGraph = isDirectedGraph;
            this.idZeroStart = idZeroStart;
            Vertices = new List<Vertex>();
            Edges = new List<Edge>();

            if (useMatrix) Matrix = new double[N, N];

            for (int i = 0; i < N; i++)
            {
                int id = idZeroStart ? i : i + 1;
                Vertex n = new Vertex(id);
                Vertices.Add(n);
            }
        }

        //隣接行列方式でのセットアップ
        public void SetAdjancencyMatrix(double[,] matrix)
        {
            Matrix = matrix;

            if (matrix.GetLength(0) != Vertices.Count || matrix.GetLength(1) != Vertices.Count)
            {
                throw new Exception("Invalid Matrix Size");
            }

            for (int i = 0; i < Vertices.Count; i++)
            {
                Vertex vertexL = Vertices[i];
                for (int j = 0; j < Vertices.Count; j++)
                {
                    if (i > j && !IsDirectedGraph) continue;
                    if (matrix[i, j] != double.PositiveInfinity)
                    {
                        Vertex vertexR = Vertices[j];
                        Edge edge = new Edge(vertexL, vertexR, matrix[i, j], IsDirectedGraph);
                        Edges.Add(edge);
                    }
                }
            }

        }


        //隣接リスト方式でのセットアップ
        public void AddAdjacencyList(int idVertex, Dictionary<int, double> adjDictionary)
        {
            int indexL = idZeroStart ? idVertex : idVertex - 1;
            Vertex vertexL = Vertices[indexL];

            foreach (var v in adjDictionary)
            {
                int idR = v.Key;
                double weight = v.Value;
                int indexR = idZeroStart ? idR : idR - 1;
                Vertex vertexR = Vertices[indexR];
                Edge edge = new Edge(vertexL, vertexR, weight, IsDirectedGraph);
                Edges.Add(edge);
                if (Matrix != null)
                {
                    Matrix[indexL, indexR] = weight;
                    if (!IsDirectedGraph) Matrix[indexR, indexL] = weight;
                }
            }
        }
        public void AddAdjacencyList(int idVertex, List<int> adjList)
        {
            Dictionary<int, double> dic = new Dictionary<int, double>();
            foreach (int x in adjList)
            {
                dic.Add(x, 1);
            }
            AddAdjacencyList(idVertex, dic);
        }

        //辺単位でのセットアップ
        public void AddEdge(int idL, int idR, int weight = 1)
        {
            int indexL = idZeroStart ? idL : idL - 1;
            int indexR = idZeroStart ? idR : idR - 1;

            Vertex vertexL = Vertices[indexL];
            Vertex vertexR = Vertices[indexR];
            Edge edge = new Edge(vertexL, vertexR, weight, IsDirectedGraph);
            Edges.Add(edge);
            if (Matrix != null)
            {
                Matrix[indexL, indexR] = weight;
                if (!IsDirectedGraph) Matrix[indexR, indexL] = weight;
            }
        }


        //開始点を指定し、幅優先探索で到達できる
        //頂点とステップ数（辺ウェイト考慮せず）のペアをDictionaryで出力
        public Dictionary<int, int> BreadthFirstSearch(int idS)
        {
            Dictionary<int, int> distances = new Dictionary<int, int>();
            distances.Add(idS, 0);
            int index = idZeroStart ? idS : idS - 1;

            List<Vertex> nodes = new List<Vertex>();
            Vertex nodeStart = Vertices[index];
            nodeStart.Visited = true;
            nodes.Add(nodeStart);
            int depth = 1;
            while (nodes.Count > 0)
            {
                List<Vertex> nextNodes = new List<Vertex>();
                foreach (Vertex node in nodes)
                {
                    foreach (Vertex cn in node.ConnectedVertices)
                    {
                        if (!cn.Visited)
                        {
                            nextNodes.Add(cn);
                            cn.Visited = true;
                            distances.Add(cn.Id, depth);
                        }
                    }
                }
                nodes = nextNodes;
                depth++;
            }
            return distances;
        }

        //開始点を指定し、深さ優先探索
        //TimeFound(訪問時刻)とTimeProcessed(処理完了時刻)を出力
        public int DepthFirstSearch(int idS, int connectGraphNo = 1, int time = 0)
        {
            Stack<Vertex> stack = new Stack<Vertex>();
            int index = idZeroStart ? idS : idS - 1;
            List<Vertex> nodes = new List<Vertex>();
            Vertex nodeStart = Vertices[index];
            nodeStart.Visited = true;

            stack.Push(nodeStart);
            nodeStart.TimeVisited = time;
            nodeStart.ConnectGraphNo = connectGraphNo;

            while (stack.Count > 0)
            {
                Vertex v = stack.Peek();

                if (v.IndexNextSearch >= v.ConnectedVertices.Count)
                {
                    time++;
                    v.TimeProcessed = time;
                    stack.Pop();
                }
                else
                {
                    Vertex vn = v.ConnectedVertices[v.IndexNextSearch];
                    if (!vn.Visited)
                    {
                        time++;
                        stack.Push(vn);
                        vn.Visited = true;
                        vn.TimeVisited = time;
                        vn.ConnectGraphNo = connectGraphNo;
                    }
                    v.IndexNextSearch++;
                }
            }
            return time;
        }

        //全頂点を探索
        public void DFS()
        {
            int time = 1;
            int connectNo = 1;
            int index = 0;
            while (index < Vertices.Count)
            {
                int id = idZeroStart ? index : index + 1;
                time = DepthFirstSearch(id, connectNo, time);

                while (index < Vertices.Count && Vertices[index].Visited) index++;
                connectNo++;
                time++;
            }
        }


        //Prim法で最小全域木を作成し重みの総和を計算(PriorityQueueクラスを利用)
        //辺に着目するためO(m log(m))であまり早くないが殆どの場合十分と思われる
        public double MinimumSpanningTree()
        {
            //最初に選ぶ頂点は何でもよい
            Vertex v0 = Vertices[0];

            bool[] includedVertex = new bool[Vertices.Count + 1];
            double sumWeight = 0;

            //最小全域木に含まれるEdge
            List<Edge> mstEdges = new List<Edge>();
            //現在の木に隣接しているEdgeの優先度付きキュー
            PriorityQueue<Edge> pq = new PriorityQueue<Edge>();

            includedVertex[v0.Id] = true;
            foreach (Edge en in v0.ConnectedEdges)
            {
                pq.Enqueue(en, -en.Weight);
            }

            int counter = 1;
            while (counter < Vertices.Count)
            {
                Edge edge = pq.Dequeue();
                Vertex v;
                //既に両方の頂点が木に含まれているEdgeは除外（含むとループができてしまう)
                if (includedVertex[edge.VertexL.Id] && includedVertex[edge.VertexR.Id]) continue;
                else if (includedVertex[edge.VertexL.Id]) v = edge.VertexR;
                else v = edge.VertexL;
                mstEdges.Add(edge);
                sumWeight += edge.Weight;
                includedVertex[v.Id] = true;

                //新しく木に隣接するようになったEdgeをキューに追加
                foreach (Edge e1 in v.ConnectedEdges)
                {
                    if (includedVertex[e1.VertexL.Id] && includedVertex[e1.VertexR.Id]) continue;
                    pq.Enqueue(e1, -e1.Weight);
                }

                counter++;
            }

            return sumWeight;
        }

        //O((|V|+|E|)ln(|V|)
        public double[] Dijkstra(int idS)
        {
            double[] distances = Enumerable.Repeat(double.PositiveInfinity, Vertices.Count + 1).ToArray();
            int index = idZeroStart ? idS : idS - 1;
            distances[idS] = 0;

            Vertex v0 = Vertices[index];

            PriorityQueue<Vertex> pq = new PriorityQueue<Vertex>();
            pq.Enqueue(v0, 0);

            while (pq.Count > 0)
            {
                Vertex v = pq.Dequeue();
                foreach (Edge e in v.ConnectedEdges)
                {
                    Vertex vn = e.VertexL == v ? e.VertexR : e.VertexL;
                    double distanceNew = distances[v.Id] + e.Weight;
                    if (distanceNew < distances[vn.Id])
                    {
                        distances[vn.Id] = distanceNew;
                        //distanceがより良い値に更新される時、
                        //同一のVertexが複数キューに入ることがあるが、
                        //先に入った（悪い方の）数値は必ず無視されるので問題ない
                        pq.Enqueue(vn, -distances[vn.Id]);
                    }
                }
            }


            return distances;
        }

        //負閉路を含む場合戻り値False
        //有向グラフ限定 重みマイナスを許容,ネガティブループ検出　O(|V||E|)
        public bool BellmanFord(int idS,out double[] distances)
        {
            distances = Enumerable.Repeat(double.PositiveInfinity, Vertices.Count + 1).ToArray();
            int index = idZeroStart ? idS : idS - 1;
            distances[idS] = 0;

            for (int i = 0; i < Vertices.Count - 1; i++)
            {
                foreach (Edge e in Edges)
                {
                    double dn = distances[e.VertexL.Id] + e.Weight;
                    if (dn < distances[e.VertexR.Id])
                    {
                        distances[e.VertexR.Id] = dn;
                    }
                }
            }

            double d1 = distances[Vertices.Count];

            for (int i = 0; i < Vertices.Count - 1; i++)
            {
                foreach (Edge e in Edges)
                {
                    double dn = distances[e.VertexL.Id] + e.Weight;
                    if (dn < distances[e.VertexR.Id])
                    {
                        distances[e.VertexR.Id] = dn;
                    }
                }
            }

            double d2= distances[Vertices.Count];

            if (d1 != d2) return false;
            else return true;


            //foreach (Edge e in Edges)
            //{                
            //    if (distances[e.VertexR.Id] > distances[e.VertexL.Id] + e.Weight)
            //    {
            //        return false;
            //    }
            //}
            //return true;
        }

        //全点対最短経路（ワ―シャル・フロイト） 要matrix O(|V|^3)
        public double[,] WarshallFloyd(out int[,] predecessorMatrix)
        {
            int N = Vertices.Count;
            double[,] d = (double[,])Matrix.Clone();

            int[,] p = new int[N, N]; //先行点行列
            for (int i = 0; i < N; i++)
            {
                for (int j = 0; j < N; j++)
                {
                    if (d[i, j] != double.PositiveInfinity && i != j)
                    {
                        p[i, j] = i; if (!idZeroStart) p[i, j]++;
                    }
                }
            }

            for (int k = 0; k < N; k++)
            {
                double[,] dn = new double[N, N];
                int[,] pn = new int[N, N];
                for (int i = 0; i < N; i++)
                {
                    for (int j = 0; j < N; j++)
                    {
                        if (i == j) continue;
                        //kを経由することで改善する場合
                        if (d[i, j] > d[i, k] + d[k, j])
                        {
                            dn[i, j] = d[i, k] + d[k, j];
                            pn[i, j] = p[k, j];
                        }
                        else
                        {
                            dn[i, j] = d[i, j];
                            pn[i, j] = p[i, j];
                        }
                    }
                }
                d = dn; p = pn;
            }
            predecessorMatrix = p;
            return d;
        }


    }

    class Vertex
    {
        public int Id { get; }
        public List<Edge> ConnectedEdges { get; private set; }
        public List<Vertex> ConnectedVertices { get; private set; }
        public bool Visited { get; set; }
        public int TimeVisited { get; set; }
        public int TimeProcessed { get; set; }
        public int IndexNextSearch { get; set; } //次に探索するVertex番号
        public int ConnectGraphNo { get; set; } //連結している頂点は同じ番号


        public Vertex(int id)
        {
            Id = id;
            ConnectedEdges = new List<Edge>();
            ConnectedVertices = new List<Vertex>();
        }


    }

    class Edge
    {
        public Vertex VertexL { get; }
        public Vertex VertexR { get; }
        public double Weight { get; }

        public Edge(Vertex vertexL, Vertex vertexR, double weight, bool isDirected)
        {
            VertexL = vertexL;
            VertexR = vertexR;
            vertexL.ConnectedEdges.Add(this);
            vertexL.ConnectedVertices.Add(vertexR);
            if (!isDirected)
            {
                vertexR.ConnectedEdges.Add(this);
                vertexR.ConnectedVertices.Add(vertexL);
            }
            Weight = weight;
        }
    }

    //優先度付きキュー（実装はヒープ)
    class PriorityQueue<T>
    {
        private List<HeapNode> heap = new List<HeapNode>();

        public int Count
        {
            get
            {
                return heap.Count;
            }
        }

        public void Enqueue(T item, double priority)
        {
            HeapNode node = new HeapNode(item, priority);
            heap.Add(node);

            int index = heap.Count - 1;

            while (index > 0)
            {
                int indexParent = (index - 1) / 2;
                HeapNode parent = heap[indexParent];
                if (priority > parent.Priority)
                {
                    Swap(index, indexParent);
                    index = indexParent;
                }
                else
                {
                    break;
                }
            }
        }

        public T Dequeue()
        {
            if (heap.Count == 0)
            {
                throw new Exception("No item in priorityQueue");
            }

            T item = heap[0].Node;
            heap[0] = heap[heap.Count - 1];
            heap.RemoveAt(heap.Count - 1);
            if (heap.Count > 0)
            {
                HeapNode node = heap[0];
                int index = 0;
                while (true)
                {
                    int indexL = 2 * index + 1;
                    int indexR = 2 * index + 2;
                    if (indexL >= heap.Count) //子なし(Leaf)
                    {
                        break;
                    }
                    else //子が存在
                    {
                        HeapNode childBetter = heap[indexL];
                        int indexBetter = indexL;
                        if (indexR < heap.Count && heap[indexL].Priority < heap[indexR].Priority)
                        {
                            childBetter = heap[indexR];
                            indexBetter = indexR;
                        }

                        if (node.Priority < childBetter.Priority)
                        {
                            Swap(index, indexBetter);
                            //node = childBetter; 参照なのでスワップで自動的に子を参照してる
                            index = indexBetter;
                        }
                        else
                        {
                            break;
                        }
                    }
                }
            }
            return item;
        }

        public T Peek()
        {
            if (heap.Count == 0)
            {
                throw new Exception("No item in priorityQueue");
            }
            return heap[0].Node;
        }

        private void Swap(int i, int j)
        {
            HeapNode hn = heap[i];
            heap[i] = heap[j];
            heap[j] = hn;
        }


        class HeapNode
        {
            public T Node { get; }
            public double Priority { get; }
            public HeapNode(T item, double priority)
            {
                Node = item;
                Priority = priority;
            }
        }

    }


}