SRM632D1Med CandyCupRunningCompetition
これ, 400 ptぐらいだと思う
問題概要(原文)
$N$ 頂点 $M$ 本の無向辺からなる無向グラフが与えられる. $i$ 番の辺の容量は $3^i$ である. $0 \rightarrow N-1$ への最大フローを $10^9+7$ で割ったあまりを求めよ.
考察
容量がクソでかいので最大流を実際に流すのは難しい.
ここで,辺の容量が全て異なることに着目しよう.あるフローが $3^k$ 流れたとする.このとき,容量 $3^k$ の辺の容量は $0$ になるが,他の辺の容量はまだまだ大きい.
よって以下のようにすればよい.
- 最大フローが $3^i$ となるようなフローがあるか $M-1$ から $0$ に向かって探す
- 流せるかどうかは未使用の辺で容量が $3^i$ 以上の辺だけをつかって $0$ から $N-1$ へ移動可能かどうかと等しい
- union-findでさっきの辺たちをつないで, $0$ と $N-1$ がつながっていたなら,流せると判定して, $i$ 番の辺に使用済みマークをつける.
計算量は $O(M^2)$
ソースコード
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Debug = System.Diagnostics.Debug;
using StringBuilder = System.Text.StringBuilder;
public class CandyCupRunningCompetition
{
public int findMaximum(int N, int[] A, int[] B)
{
const long MOD = (long)1e9 + 7;
var m = A.Length;
var powed = new long[m + 1];
powed[0] = 1;
for (int i = 1; i <= m; i++)
powed[i] = (powed[i - 1] * 3) % MOD;
long ans = 0;
var used = new bool[m];
for (int i = m - 1; i >= 0; i--)
{
var s = new DisjointSet(N);
for (int j = i; j < m; j++)
{
if (used[j]) continue;
s.Unite(A[j], B[j]);
}
if (s.IsUnited(0, N - 1))
{
ans = (ans + powed[i]) % MOD;
used[i] = true;
}
}
return (int)ans;
}
}
#region DisjointSet
public class DisjointSet
{
public int[] par, ranks, count;
public DisjointSet(int n)
{
par = new int[n];
count = new int[n];
for (int i = 0; i < n; i++)
{
par[i] = i;
count[i] = 1;
}
ranks = new int[n];
}
public int this[int id] { get { return (par[id] == id) ? id : this[par[id]]; } }
public bool Unite(int x, int y)
{
x = this[x]; y = this[y];
if (x == y) return false;
if (ranks[x] < ranks[y])
{
par[x] = y;
count[y] += count[x];
}
else
{
par[y] = x;
count[x] += count[y];
if (ranks[x] == ranks[y])
ranks[x]++;
}
return true;
}
public int Size(int x) { return count[this[x]]; }
public bool IsUnited(int x, int y) { return this[x] == this[y]; }
}
#endregion
コメント
- $10^9+7 \times 3$ はオーバーフローしうるので注意しよう(modを取るときはlongに限るなぁ)