AtCoder Regular Contest 129 D - -1+2-1 解説 (エスパー→証明)
厳密ではありませんが証明はちゃんとつけました。
問題リンク
https://atcoder.jp/contests/arc129/tasks/arc129_d
問題概要
長さ の整数列 があります。この整数列の両端は繋がっています。
にそれぞれ を何回か足すことによって を全て にできるか判定し、できるなら最小回数を求めて下さい。
制約
- 入力は全て整数
考察
今回は自分の解法を解説するので kmjp さんの解説と同じ内容は省きます。
行列を考えます。 を選ぶ回数を とおくと、次のような関係が成り立ちます。
ただし、。また、 は
\begin{pmatrix} -2 & 1 & 0 & \cdots & 0 & 1 \\ 1 & -2 & 1 & \cdots & 0 & 0 \\ 0 & 1 & -2 & \cdots & 0 & 0 \\ \vdots & \vdots & \vdots & \ddots & \vdots & \vdots \\ 0 & 0 & 0 & \cdots & -2 & 1 \\ 1 & 0 & 0 & \cdots & 1 & -2 \\ \end{pmatrix}
ここで、 の rank を調べると となります。つまり、 個の を決めれば残りの 個も決まります。
よって、適当な主小行列 (行列の 行目と 列目を同時になくした行列) の逆行列を考えたら上手に の値が決まりそうな雰囲気がします。
一方、設定から自明に なので、rank が であることに由来する制約条件も分かるので、これは妥当な考察であることが分かります。
解法
先述の行列 のうち 行目 列目を取り除いた主小行列を とおきます。つまり、
\begin{pmatrix} -2 & 1 & 0 & \cdots & 0 \\ 1 & -2 & 1 & \cdots & 0 \\ 0 & 1 & -2 & \cdots & 0 \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & 0 & \cdots & -2 \\ \end{pmatrix}
です。また の第 成分をなくしたものを とおきます。
の rank は なので、 は逆行列を持ち、 が決まれば も一意に決まります。また、 が定まれば連立方程式の適当などれか (例えば ) を用いることで も定まります。
さて、 ですが、Wolfram Alpha に入れると次のようになります。これから、 成分 が である対称行列、つまり 成分は一般に である対称行列となることが推測されます。
実際に の各成分が であることは証明できます。
証明を展開する
の 第 成分 ( のいずれかが ) を と仮定すると の自然な拡張となるので、そうします。
の第 成分を とおき ()、これが に一致することを示します。ただし、 はクロネッカーのデルタです。
と表せます。これは先においた仮定により成立します。
- のとき
となり、これを展開して計算すると 。 - のとき
となり、これを展開して計算すると 。 - のとき
対称性より自明。
以上より、、つまり両者を掛けると単位行列になるので題意が示された。
これにより、 が綺麗な形で計算できるので、累積和や Segment Tree などによって容易に を求めることができます。
ただし、これによって求めた解は負の値が含まれている可能性があります。全ての操作を同じ回数だけ増やしても連立方程式の解となるので、最小値が負の値なら を に置き換えます。これが求めるべき最小値です。
また、解が存在しない条件は、 でない、もしくは求めた の値が整数とならないことです。
実装
実装を展開する
public override void Solve() { var n = sr.ReadInt(); var a = sr.ReadLongArray(n); if (a.Sum() != 0) { Console.WriteLine(-1); return; } var seg1 = new Segtree<long, Op>(a.Select((p, i) => -p * (n - i - 1)).ToArray()); var seg2 = new Segtree<long, Op>(a.Select((p, i) => -p * (i + 1)).ToArray()); var x = new long[n]; for (int i = 0; i < n - 1; i++) { x[i] += (i + 1) * seg1.Prod(i, n - 1); x[i] += (n - 1 - i) * seg2.Prod(0, i); if (x[i] % n != 0) { Console.WriteLine(-1); return; } x[i] /= n; } x[n - 1] = 2 * x[0] - x[1] + a[0]; if (x[n - 1] % n != 0) { Console.WriteLine(-1); return; } var min = Max(0, -x.Min()); for (int i = 0; i < n; i++) { x[i] += min; } Console.WriteLine(x.Sum()); } public readonly struct Op : ISegtreeOperator<long> { public long Operate(long x, long y) => x + y; public long Identity => 0L; }
ACコード: https://atcoder.jp/contests/arc129/submissions/37407234
感想
なんでこれ通るんですか?感のある解答でした。ちゃんと証明したのでスッキリしました。ところで の値が整数とならないことってあるんですかね……。