こどふぉ特有の、ややギャグ要素ありの楽しい問題。結構好き。
問題概要
長さ の正の整数からなる広義単調増加な数列 が与えられる。以下の操作を何度か行うことで、広義単調増加ではない状態にしたい。
- 数列の隣接する 2 つの要素を選んで、それらの XOR 和をとった値で置き換える (数列の要素数は 1 減少する)
操作回数の最小値を求めよ。実現不可能な場合は -1 を出力せよ。
制約
考えたこと
まず最初に、次のような 解法を思いついた。操作によって出来上がった数列において、隣接要素 が となったとする。このとき、もとの数列 の隣接する区間 , があって
というふうになっているはずである。この状態を実現するための操作回数は 回となる。
よって、 を全探索するという の解法が生えた。
実はほとんど 1 回や 2 回でできるのでは
ここから先がなかなかわからなかった。ちょっと例によって、「苦手な高速化」が要求されているかもしれないという悩みに取り憑かれたかもしれない。
さて、色々手を動かしてみると、なんと割と大体のケースで 1 回で実現できてしまうことがわかった。逆にできないケースを作ろうとすると、こんなケースになる (二進法で書いてる)。
10 0000000001 0000000010 0000000100 0000001000 0000010000 0000100000 0001000000 0010000000 0100000000 1000000000
この場合は、どうやっても後ろの方の「より上位の桁の 1」を相殺して消すことができないので不可能。
でもこのケースを考えたことでちょっとわかってきた。 個の整数で不可能な場合を作ろうとすると、 桁くらい必要になるのではないかという直感が働いたのだ。
さらによく考えると、やはりほとんどのケースで 1 回で済むことがわかった。たとえば
0001001101 0001011011 0001101110
のように、「最上位の 1 の桁」が一致するものが三連続で続くと、後半 2 つの XOR 和をとると「最上位の 1」が消えて小さくなるのだ。よって 1 回でできる。
ちゃんと見積もってみる。今回の制約では、最大でも 31 桁なので、 とすれば、鳩の巣原理より、「最上位の 1 の桁」が一致する 3 整数が存在することになる。
よって、次のようにすれば OK。
- 程度では の全探索
- では 1 が答え
コード
#include <bits/stdc++.h> using namespace std; int N; vector<long long> A, S; const int INF = 1<<29; long long naive() { int res = INF; for (int a = 0; a < N; ++a) { for (int b = a+1; b <= N; ++b) { for (int c = b+1; c <= N; ++c) { if ((S[a]^S[b]) > (S[b]^S[c])) { res = min(res, c-a-2); } } } } return (res < INF ? res : -1); } long long solve() { if (N < 500) return naive(); else return 1; } int main() { while (cin >> N) { A.resize(N), S.assign(N+1, 0); for (int i = 0; i < N; ++i) { cin >> A[i]; S[i+1] = S[i] ^ A[i]; } cout << solve() << endl; } }