ARC044-B:最短路問題
問題
https://beta.atcoder.jp/contests/arc044/tasks/arc044_b
N 頂点のグラフが与えられる。頂点 1 から頂点 i への最短路を a_i とするように辺を張るとき、辺の張り方は何通りか求めよ。
考え方
自明なケースを除く。まず 1 から 1 への最短路は 0 であるから、頂点 1 の最短路が 0 以外の場合は 0 通りである。また 1 以外の頂点で最短路が 0 にすることはできないので、1 以外で最短路が 0 となっている頂点が存在する場合は 0 通りである。また、 最短路が連続になっていない頂点が存在する場合、つまり最短路が i と i+2 となっている場合は、最短路が i+2 になるように辺を張ることはできないので 0 通りである。それ以外のケースについて考える。
まず頂点 1 から最短路が 1 となっている頂点には必ず辺を張らないといけない。これは 1 通りで確定する。最短路が同一の頂点に関しては辺を張っても張らなくてもよい。辺の数は同一の最短路の頂点数を k とすると k(k-1)/2 である。それぞれの辺について、張る・張らないという2通りの辺の張り方が考えられるため、辺の張り方は 2k(k-1)/2 通り存在する。
次に辺の最短路が i-1 と i の頂点の辺の張り方について考える。最短路が i の頂点から i-1 への頂点には少なくとも 1 本は辺を張らないといけない。逆に言うと辺を 1 本以上張ることができる。この時の辺の張り方は最短路が i-1 の頂点の数を l とすると、 2l - 1 通り存在する。最短路が i の頂点数が m あるとき、それぞれについて 2l - 1 通り存在するので全体としては (2l - 1)m 通りとなる。
public void solve(int testNumber, InputReader in, PrintWriter out) { Map<Long, Long> map = new TreeMap<>(); int n = in.nextInt(); long[] a = in.nextLongArray(n); if (a[0] != 0) { out.println(0); return; } for (int i = 1; i < n; i++) { if (a[i] == 0) { out.println(0); return; } } for (long l : a) { map.merge(l, 1L, Long::sum); } long ans = 1; if (map.containsKey(1L)) { long nc = map.get(1L); ans = ans % MOD * power(2, nc*(nc-1)/2, MOD); } for (long i = 2; i < n; i++) { long nc = map.containsKey(i) ? map.get(i) : 0; long pc = map.containsKey(i-1) ? map.get(i-1) : 0; ans = ans % MOD * power((power(2, pc, MOD) - 1), nc, MOD); ans = ans % MOD * power(2, nc*(nc-1)/2, MOD); } out.println(ans % MOD); } static long power(long a, long e, long modP) { long ret = 1; for (; e > 0; e /= 2) { if (e % 2 != 0) { ret = (ret * a) % modP; } a = (a * a) % modP; } return ret; }
ポイント
- 数え上げはもれなく
- 二分累乗