Problem:
Score : 700 points

Problem Statement

We have N gemstones labeled 1 through N.

You can perform the following operation any number of times (possibly zero).

Select a positive integer x, and smash all the gems labeled with multiples of x.

Then, for each i, if the gem labeled i remains without getting smashed, you will receive a_i yen (the currency of Japan).
However, a_i may be negative, in which case you will be charged money.

By optimally performing the operation, how much yen can you earn?

Constraints

All input values are integers.

1 \leq N \leq 100

|a_i| \leq 10^9

Input

Input is given from Standard Input in the following format:

N
a_1 a_2 ... a_N

Output

Print the maximum amount of money that can be earned.

Sample Input 1

6
1 2 -6 4 5 3

Sample Output 1

12

It is optimal to smash Gem 3 and 6.

Sample Input 2

6
100 -100 -100 -100 100 -100

Sample Output 2

200

Sample Input 3

5
-1 -2 -3 -4 -5

Sample Output 3

0

It is optimal to smash all the gems.

Sample Input 4

2
-1000 100000

Sample Output 4

99000

Solution in C# (Mono-mcs 6.8.0.105) (judge verdict: Accepted)

using System;
using System.Collections;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Threading.Tasks;

/*
・概要
：有向グラフに於ける最大流問題の解決に使用。
	最小カット問題も解くことができる。（最大フロー　＝　最小カット）
	より高速で動く。

：計算量は、O(V * E^2)　or　O(V^2 * E)
	（※正確には未確認）
	（頂点の数がV、辺の数がE）

：cf. 最大流問題とは。
	各辺に、そこを通ることができる「重みの上限値・最大値」がついているとして、
	始点Sから終点Gまでに、「最大でどれだけの重みを流すことができるか」、という問題。
	但し、途中で滞ってはならない。（「待ち」があってはならない。）
	（e.g. 物流：SからGまでの複数の道路を使って、なるべく多くの荷物を運ぶ方法。）

・使用法
1. 名前空間MaxFlowAlgorithm上のクラスGraphのインスタンスを作成。
	引数は、全体のノード数（頂点の数）。
	（e.g. MaxFlowAlgorithm.Graph graph = new MaxFlowAlgorithm.Graph(V);）

2. AddEdge関数で各辺を追加する。（有向なので順番に注意）
	引数は、始点、終点、重み。
	（e.g. graph.AddEdge(s,t,w);）

3. MaxFlow関数で、最大流を取得
	引数は、最大流を求めたい範囲の始点と終点
	（e.g. int ans = graph.MaxFlow(start,goal);）


・cf.「最小カット」問題
	：「最小カット」とは。
		始点Sから終点Tに向かうフローを「全て断ち切る」として、
		その際に切断する必要がある最小フロー容量のこと。
		（e.g. 重みが全て1の場合、切断する必要がある辺の数と等しくなる）

	：「最大フロー最小カット定理」
		始点Sから終点Tへの最小カット（最小の「s-tカット」）は、
		SからTへの最大フローと等しい。
		（「最大フロー　<= カット容量」、なので、最小値は最大フローと同じ）

	：帰着
		最大流問題のアルゴリズムを利用して、最小カット問題を解くことが可能。

--- !!! ---
※有向グラフなので、辺の追加の際には引数の順番に注意せよ！！！
--- !!! ---

*/

class Program
{
	//Main
	static void Main(string[] args)
	{
		//読み込む
		int N = int.Parse(Console.ReadLine());
		long[] an = Console.ReadLine().Split(' ').Select(i => long.Parse(i)).ToArray();

		//iを破壊した際に、iの倍数も破壊されることを意味するペナルティ
		long inf = 100000000000;

		//1. MaxFlowAlgorithm.Graphクラスのインスタンスを作成
		//引数は、全体のノード数（頂点の数）
		/*
		1 ~ Nまでの整数と、始点・終点
		*/
		MaxFlowAlgorithm.Graph graph = new MaxFlowAlgorithm.Graph(N+2);

		long ans = 0;
		//始点と終点
		/*
		memo
		所謂「Project Selection Problem」を、最小カット問題に帰着。

		（この問題の場合、）
		始点に繋がっている頂点は破壊されたことを、
		終点に繋がっている頂点は生存していることを意味する。

		即ち、
		始点に繋がっている辺を断つことを、生存の場合のコスト、
		終点に繋がっている辺を断つことを、破壊の場合のコスト、と捉える。

		また、両立があり得ない二頂点間には、無限のコスト（ペナルティ）を課す。
		（e.g. iが破壊されて、iの倍数が生存していることは有り得ない）

		そうすると、全ての点をどちらかに割り振る際に断絶される辺の重みを最小化する問題になる。
		（コストが最小になるような、２集団への割振り方法。）
		*/
		int source = N;
		int target = source + 1;

		//2. 各辺を追加
		for(int i=0; i<N; i++){
			//a. aiが正の場合
			if(an[i] >= 0){
				/*
				破壊した際に、aiだけのペナルティを受けると解釈する
				（得られた筈のaiを失う、と捉える（逸失利益））
				それを打ち消す為に、事前に答えにaiを足しておく
				*/
				//ペナルティを辺で再現
				graph.AddEdge(i, target, an[i]);

				//事前に答えに加算
				ans += an[i];
			}
			//b. aiが負の場合
			else{
				/*
				今度は生存した場合に、-aiだけのペナルティが発生すると考える
				（-aiだけ支払う必要）
				*/
				graph.AddEdge(source, i, -an[i]);
			}

			//iを破壊した際の効果
			/*
			（iを破壊したのに）iの倍数が生存していることはあり得ないので、
			iが生存かつiの倍数が生存するルートに、無限のペナルティを課す
			*/
			for(int j=i+1; j<=N; j+=(i+1)){
				graph.AddEdge(i, j-1, inf);
			}
		}

		//3. 現在の状態での最大流を求める
		//最小カット（最小コスト）を引いたものが、答え。
		ans -= graph.MaxFlow(source,target);

		Console.WriteLine(ans);
	}
}

//最大流問題を解く為のの名前空間
/*
2つのクラスを内包
・辺を意味するEdge
・グラフとして問題を解くGraph
*/

/*
全てclass Graphからアクセスのこと
（e.g.
・初期化
	MaxFlowAlgorithm.Graph maxPair = new MaxFlowAlgorithm.Graph(N+N+2);

・エッジの追加
	maxPair.AddEdge(start,goal,1);
）
*/

namespace MaxFlowAlgorithm
{
	public class Graph
	{
    public readonly List<Edge>[] G;
    readonly int[] lv, iter;

		//コンストラクタ
		/*
		全体のノード数で初期化
		*/
    public Graph(int V)
    {
      G = new List<Edge>[V];
      lv = new int[V];
      iter = new int[V];
      for (int i = 0; i < G.Length; i++)
          G[i] = new List<Edge>();
    }

		//辺を追加
		/*
		引数：出発点、行き先、重み（その辺の最大流）
		（＝始点、終点、フロー）
		*/
    public void AddEdge(int from, int to, long cap)
    {
      addEdge(from, to, cap, 0);
    }

    public void AddUndirectedEdge(int from, int to, long cap)
    {
      addEdge(from, to, cap, cap);
    }

		//辺を繋ぐサブメソッド
    void addEdge(int f, int t, long c1, long c2)
    {
      var a = new Edge(t, c1);
      var b = new Edge(f, c2);
      Edge.Link(a, b);
      G[f].Add(a);
      G[t].Add(b);
    }

		//最大流を返す
		/*
		引数：出発点と行き先（スタートとゴール）
		*/
    public long MaxFlow(int src, int sink, long f = -1)
    {
      long flow = 0;
      if (f < 0) f = long.MaxValue;
      while (f > 0)
      {
        bfs(src);
        if (lv[sink] == 0) return flow;
        Array.Clear(iter, 0, iter.Length);
        long df;
        while ((df = dfs(src, sink, f)) > 0) { flow += df; f -= df; }
      }
      return flow;
    }

    void bfs(int s)
    {
      Array.Clear(lv, 0, lv.Length);
      var q = new Queue<int>();
      lv[s] = 1;
      q.Enqueue(s);
      while (q.Count > 0)
      {
        var v = q.Dequeue();
        foreach (var e in G[v])
            if (e.Cap > 0 && lv[e.To] == 0)
            {
              lv[e.To] = lv[v] + 1;
              q.Enqueue(e.To);
            }
      }

    }
    long dfs(int v, int t, long f)
    {
      if (v == t) return f;
      long ret = 0;
      for (; iter[v] < G[v].Count; iter[v]++)
      {
        var e = G[v][iter[v]];
        if (e.Cap <= 0 || lv[v] >= lv[e.To]) continue;
        long df = dfs(e.To, t, Math.Min(f, e.Cap));
        if (df <= 0) continue;
        e.Cap -= df;
        e.Rev.Cap += df;
        ret += df; f -= df;
        if (f == 0) break;
      }
      return ret;

    }
	}

	//辺
	public class Edge
	{
    public static void Link(Edge e1, Edge e2)
    {
      e1.Rev = e2; e2.Rev = e1;
    }
    public int To { get; private set; }
    public Edge Rev { get; private set; }
    public long Cap { get; set; }
    public Edge(int t, long c)
    {
      To = t;
      Cap = c;
    }
    public override string ToString()
    {
      return string.Format("to: {0}, cap: {1}", To, Cap);
    }
	}
}