こどふぉ特有の 回以下の操作で〜を達成せよ、という問題
問題概要
長さ の正の整数からなる数列
が与えられる。以下の操作を
回以下繰り返すことで、数列の値がすべて等しくなるようにしたい。そのような操作列を一つ求めよ。不可能である場合は -1 を出力せよ。
[操作]:整数 を選び、
の値を
だけ減少させて、
の値を
だけ増加させる。ただし
の値が負になってはいけない
制約
考えたこと
まず、操作をどのように行っても、 の総和が不変であることに注目する。よって、明らかに
の総和
が
の倍数でなければならない。
このとき とする。すべての要素をうまいこと
に揃えたい。色々迷走してしまったが、最終的にたどり着いたのは、次の方針だ。
- フェーズ I:
以外のすべての要素を 0 にする (それが可能であることを後に示す)
- フェーズ II:
の要素を各要素に分配していって実現する
フェーズ I が実現可能ならば、フェーズ II は簡単だ。単に から各
に対して
として操作を行なっていけばよい。
フェーズ I は、実は の順序で行なっていけばよいのだ。最悪ケースは
のように、ひたすら 1 が並ぶケース。しかし であることに注目しよう。そうすると、
であることが保証される。よって、一旦 から
へ要素を移して
の状態にしてから、
から
に戻せば
の状態になる。
より一般に、 の順に
が
の倍数になるように、
から適切な量を
に配る
が 0 になるように
から
へ配る
とすれば OK。これで最大でも 回の操作で実現できる。
コード
#include <bits/stdc++.h> using namespace std; void solve(vector<long long> &A) { int N = A.size(); long long sum = 0; for (auto v: A) sum += v; if (sum % N != 0) { cout << -1 << endl; return; } long long num = sum / N; // 0-indexed vector<long long> ri, rj, rx; auto run = [&](int i, int j, long long x) -> void { //cout << i+1 << " " << j+1 << " " << x << endl; if (x == 0) return; ri.push_back(i+1), rj.push_back(j+1), rx.push_back(x); A[i] -= x * (i+1); A[j] += x * (i+1); }; // 0 に集める for (int i = 1; i < N; ++i) { long long p = A[i] / (i+1), r = A[i] % (i+1); if (r == 0) run(i, 0, p); else { run(0, i, i+1-r); run(i, 0, A[i]/(i+1)); } } // 0 から揃えていく for (int i = 1; i < N; ++i) { long long dif = num - A[i]; run(0, i, dif); } // 出力 cout << ri.size() << endl; for (int i = 0; i < ri.size(); ++i) { cout << ri[i] << " " << rj[i] << " " << rx[i] << endl; } } int main() { cin.tie(0); ios::sync_with_stdio(false); int T; cin >> T; while (T--) { int N; cin >> N; vector<long long> A(N); for (int i = 0; i < N; ++i) cin >> A[i]; solve(A); } }