外部サイトへ移動します

リンク先の URL を確認してから開いてください。

別タブで開く

解説

F - It Looks Difficult

作成者 kammyu、yesantikiss、yuki4869

配点 100 点

実行時間制限
2 秒
メモリ制限
512 MiB

解法1

f(a,b)f(a,b) が最大何回の変形で b=0b=0 にたどり着くかを考えてみてみましょう。
まず、

  • a0=a,b0=ba_0=a,b_0=b
  • an=an−1 xor bn−1a_n=a_{n-1}\ \text{xor}\ b_{n-1}
  • bn=2×(an−1 and bn−1)b_n=2\times(a_{n-1}\ \text{and}\ b_{n-1})

とします。

ここで、ある非負整数 xx を 22 進数表記した時に立っているビットの数を popcount(x)\text{popcount}(x) とします。
xor,and\text{xor},\text{and} の性質から、xor\text{xor} で打ち消し合ったビットが and\text{and} に立つので、

popcount(an)+popcount(bn)=popcount(an+1)+2×popcount(bn+1)≥popcount(an+1)+popcount(bn+1)\begin{aligned} \text{popcount}(a_n)+\text{popcount}(b_n)&=\text{popcount}(a_{n+1})+2\times\text{popcount}(b_{n+1})\\ &\ge\text{popcount}(a_{n+1})+\text{popcount}(b_{n+1}) \end{aligned}

であることがすぐに分かります。

ここで、等号が成り立つのは bn+1=0b_{n+1}=0 の時のみであり、追加で 11 回変形を行うと、 f(a,b)=f(an+1,0)=an+1f(a,b)=f(a_{n+1},0)=a_{n+1} を導くことができます。

あとは bn+1≠0b_{n+1}\ne0 の場合について何回の変形で計算が終了するかが分かればよく、

popcount(an)+popcount(bn)>popcount(an+1)+popcount(bn+1)\text{popcount}(a_n)+\text{popcount}(b_n) >\text{popcount}(a_{n+1})+\text{popcount}(b_{n+1})

より、最大で popcount(a)+popcount(b)\text{popcount}(a)+\text{popcount}(b) 回程度の変換で bn=0b_n=0 にたどり着くことが分かります。

以上より、O(log⁡a+log⁡b)O(\log a+\log b) 回の変換で b=0b=0 になることが分かりました。

再帰関数等を用いて問題文の f(a,b)f(a,b) をそのまま愚直に実装します。
計算量は O(Nlog⁡(X+∑A))O(N\log(X+\sum A)) です。

解法2

実は、22 進数の足し算の筆算を考えることで、f(a,b)=a+bf(a,b)=a+b が分かります。
より厳密には、以下の議論によって f(a,b)=a+bf(a,b)=a+b を証明することができます。

f(x,i)f(x,i) を非負整数 xx を 22 進数表記したときの ii 桁目とします。

x=∑i=N∞f(x,i)2ix=\sum_{i=N}^\infty f(x,i)2^i

より、xor,and\text{xor},\text{and} はビットごとに独立であることから、

a xor b=(∑i=N∞f(a,i)2i) xor (∑i=N∞f(b,i)2i)=∑i=N∞(f(a,i) xor f(b,i))2ia and b=(∑i=N∞f(a,i)2i) and (∑i=N∞f(b,i)2i)=∑i=N∞(f(a,i) and f(b,i))2i\begin{aligned} a\text{ xor }b&=\left(\sum_{i=N}^\infty f(a,i)2^i\right)\text{ xor }\left(\sum_{i=N}^\infty f(b,i)2^i\right)\\ &=\sum_{i=N}^\infty(f(a,i)\text{ xor }f(b,i))2^i\\ a\text{ and }b&=\left(\sum_{i=N}^\infty f(a,i)2^i\right)\text{ and }\left(\sum_{i=N}^\infty f(b,i)2^i\right)\\ &=\sum_{i=N}^\infty(f(a,i)\text{ and }f(b,i))2^i \end{aligned}

が成り立ちます。
そして、x,y∈{0,1}x,y\in\{0,1\} に対して x+y=x xor y+2×(x and y)x+y=x\text{ xor }y+2\times(x\text{ and }y) であることは少し考えればわかるので、上の式と併せて以下が成り立つことが分かります。

a xor b+2×(a and b)=∑i=N∞(f(a,i) xor f(b,i))2i+2×∑i=N∞(f(a,i) and f(b,i))2i=∑i=N∞(f(a,i) xor f(b,i)+2×(f(a,i) and f(b,i)))2i=∑i=N∞(f(a,i)+f(b,i))2i=∑i=N∞f(a,i)2i+∑i=N∞f(b,i)2i=a+b\begin{aligned} a\text{ xor }b+2\times(a\text{ and }b)&=\sum_{i=N}^\infty(f(a,i)\text{ xor }f(b,i))2^i+2\times\sum_{i=N}^\infty(f(a,i)\text{ and }f(b,i))2^i\\ &=\sum_{i=N}^\infty(f(a,i)\text{ xor }f(b,i)+2\times(f(a,i)\text{ and }f(b,i)))2^i\\ &=\sum_{i=N}^\infty(f(a,i)+f(b,i))2^i\\ &=\sum_{i=N}^\infty f(a,i)2^i+\sum_{i=N}^\infty f(b,i)2^i\\ &=a+b \end{aligned}

また、解法1の後半と同様の考察をすることで有限回の変換によって計算結果 a+ba+b が得られることも分かります。

よって、B1=X+A1B_1=X+A_1 に注意して累積和を計算すればよいです。

計算量は O(N)O(N) です。

解法1の実装例(C++)

#include <bits/stdc++.h>
using namespace std;
long long f(long long a, long long b)
{
  if (b == 0) return a;
  return f(a ^ b, 2 * (a & b));
}
int main()
{
  long long N, X, a;
  cin >> N >> X;
  while (N--)
    cin >> a, X = f(X, a), cout << X << " ";
}

解法2の実装例(C++)

#include <bits/stdc++.h>
using namespace std;
int main()
{
  long long N, X, a;
  cin >> N >> X;
  while (N--)
    cin >> a, X += a, cout << X << " ";
}

ユーザー解説

ユーザー解説を書く

ユーザー解説はまだありません。