Problem p03553 (C++)

Buggy submission (status: Time Limit Exceeded):
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>
#define int long long
using namespace std;

struct Edge {
  int from, to, flow, rev;
  Edge() {}
  Edge(int from, int to, int flow, int rev) {
    this->from = from;
    this->to = to;
    this->flow = flow;
    this->rev = rev;
  }
};
typedef vector<Edge> Edges;
typedef vector<Edges> Graph;

const int VMAX = 100;

void add_edge(Graph &g, int s, int t, int flow) {
  g[s].push_back(Edge(s, t, flow, g[t].size()));
  g[t].push_back(Edge(t, s, 0, g[s].size() - 1));
}

int dfs(Graph &g, int s, int t, int level[], int seenEdgeCount[], int flow) {
  if (s == t)
    return flow;

  for (int &i = seenEdgeCount[s]; i < g[s].size(); i++) {
    int nv = g[s][i].to;
    if (level[nv] != level[s] + 1 || g[s][i].flow <= 0)
      continue; // 辺残量も確認する！
    int f = dfs(g, nv, t, level, seenEdgeCount, min(flow, g[s][i].flow));
    if (f <= 0)
      continue;
    // s --> nv間にfだけ流す
    g[s][i].flow -= f;
    g[nv][g[s][i].rev].flow += f;
    return f;
  }
  return 0;
}

int maxflow(Graph g, int s, int t) {
  int i, level[VMAX], ret = 0;

  while (true) {
    for (i = 0; i < g.size(); i++)
      level[i] = -1;
    queue<int> que;

    level[s] = 0;
    que.push(s);
    while (!que.empty()) {
      int v = que.front();
      que.pop();
      for (i = 0; i < g[v].size(); i++) {
        if (g[v][i].flow <= 0)
          continue;
        int nv = g[v][i].to;
        if (level[nv] == -1) {
          level[nv] = level[v] + 1;
          que.push(nv);
        }
      }
    }
    if (level[t] == -1)
      break;

    int seenEdgeCount[VMAX] = {0};

    int flowINF = 1e+11; // 辺容量の最大値よりも大きな値にする

    while (true) {
      int f = dfs(g, s, t, level, seenEdgeCount, flowINF);
      if (f <= 0)
        break;
      ret += f;
    }
  }
  return ret;
}

int n;
int a[101];
Graph g;

signed main() {
  int i, j;

  cin >> n;
  for (i = 0; i < n; i++)
    cin >> a[i + 1];

  // 各宝石を割るかどうかを独立に選べるとしたら、最大いくら貰えるか
  int risou = 0;
  for (i = 1; i <= n; i++) {
    if (a[i] > 0)
      risou += a[i]; // 価値が正の宝石だけ割る
  }

  // そこからいくら引かれるか
  // （各宝石を割るグループ, 割らないグループに分けたとき,
  // いくらペナルティーが発生するか）<-> S--Tカットと対応させる！ S --
  // Tカットの容量は、S側からT側に張られた辺のコストの和で定義される。T側からS側に張られた辺のコストはカウントしないことに注意。
  g.resize(n + 2);

  // 割るグループS(頂点0) --> 割らないグループT(頂点n + 1)
  for (i = 1; i <= n; i++) {
    if (a[i] < 0)
      add_edge(g, 0, i, -a[i]);
  }

  int flowINF = 1e+11;
  for (i = 1; i <= n; i++) {
    for (j = 2 * i; j <= n; j += i) {
      // i --> jと辺をはる条件 …
      // 「iを割る⇒jを割る」。違反をする＝S->T辺を切る、という風に対応づける。
      add_edge(g, i, j, flowINF);
    }
  }

  for (i = 1; i <= n; i++) {
    if (a[i] > 0)
      add_edge(g, i, n + 1, a[i]);
  }

  // 最小カットの容量と、最大流で流れた量は等しい
  int penalty = maxflow(g, 0, n + 1);

  cout << risou - penalty << endl;
  return 0;
}

Fixed submission:
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>
#define int long long
using namespace std;

struct Edge {
  int from, to, flow, rev;
  Edge() {}
  Edge(int from, int to, int flow, int rev) {
    this->from = from;
    this->to = to;
    this->flow = flow;
    this->rev = rev;
  }
};
typedef vector<Edge> Edges;
typedef vector<Edges> Graph;

const int VMAX = 102;

void add_edge(Graph &g, int s, int t, int flow) {
  g[s].push_back(Edge(s, t, flow, g[t].size()));
  g[t].push_back(Edge(t, s, 0, g[s].size() - 1));
}

int dfs(Graph &g, int s, int t, int level[], int seenEdgeCount[], int flow) {
  if (s == t)
    return flow;

  for (int &i = seenEdgeCount[s]; i < g[s].size(); i++) {
    int nv = g[s][i].to;
    if (level[nv] != level[s] + 1 || g[s][i].flow <= 0)
      continue; // 辺残量も確認する！
    int f = dfs(g, nv, t, level, seenEdgeCount, min(flow, g[s][i].flow));
    if (f <= 0)
      continue;
    // s --> nv間にfだけ流す
    g[s][i].flow -= f;
    g[nv][g[s][i].rev].flow += f;
    return f;
  }
  return 0;
}

int maxflow(Graph g, int s, int t) {
  int i, level[VMAX], ret = 0;

  while (true) {
    for (i = 0; i < g.size(); i++)
      level[i] = -1;
    queue<int> que;

    level[s] = 0;
    que.push(s);
    while (!que.empty()) {
      int v = que.front();
      que.pop();
      for (i = 0; i < g[v].size(); i++) {
        if (g[v][i].flow <= 0)
          continue;
        int nv = g[v][i].to;
        if (level[nv] == -1) {
          level[nv] = level[v] + 1;
          que.push(nv);
        }
      }
    }
    if (level[t] == -1)
      break;

    int seenEdgeCount[VMAX] = {0};

    int flowINF = 1e+11; // 辺容量の最大値よりも大きな値にする

    while (true) {
      int f = dfs(g, s, t, level, seenEdgeCount, flowINF);
      if (f <= 0)
        break;
      ret += f;
    }
  }
  return ret;
}

int n;
int a[101];
Graph g;

signed main() {
  int i, j;

  cin >> n;
  for (i = 0; i < n; i++)
    cin >> a[i + 1];

  // 各宝石を割るかどうかを独立に選べるとしたら、最大いくら貰えるか
  int risou = 0;
  for (i = 1; i <= n; i++) {
    if (a[i] > 0)
      risou += a[i]; // 価値が正の宝石だけ割る
  }

  // そこからいくら引かれるか
  // （各宝石を割るグループ, 割らないグループに分けたとき,
  // いくらペナルティーが発生するか）<-> S--Tカットと対応させる！ S --
  // Tカットの容量は、S側からT側に張られた辺のコストの和で定義される。T側からS側に張られた辺のコストはカウントしないことに注意。
  g.resize(n + 2);

  // 割るグループS(頂点0) --> 割らないグループT(頂点n + 1)
  for (i = 1; i <= n; i++) {
    if (a[i] < 0)
      add_edge(g, 0, i, -a[i]);
  }

  int flowINF = 1e+11;
  for (i = 1; i <= n; i++) {
    for (j = 2 * i; j <= n; j += i) {
      // i --> jと辺をはる条件 …
      // 「iを割る⇒jを割る」。違反をする＝S->T辺を切る、という風に対応づける。
      add_edge(g, i, j, flowINF);
    }
  }

  for (i = 1; i <= n; i++) {
    if (a[i] > 0)
      add_edge(g, i, n + 1, a[i]);
  }

  // 最小カットの容量と、最大流で流れた量は等しい
  int penalty = maxflow(g, 0, n + 1);

  cout << risou - penalty << endl;
  return 0;
}