Official

C - Submask Editorial by physics0523


以下の解説で、 \(2\) 進表記での \(2^k\) の位を \(k\) bit 目と呼びます。

解法1. 集合を徐々に大きくして構成する

以下を順次行うことにより、答えを構成することができます。

  • 最初、 \(S=\{0\}\) とする。
  • \(i=0,1,\dots,59\) まで以下を繰り返す。
    • もし \(N\)\(i\) bit 目が \(1\) なら、以下の更新を行う。
      • \(S\) の全ての要素 \(x\) について同時に、 \(x\)\(i\) bit 目を \(1\) にした新たな要素 \(y\) を追加する。

実装例(C++):

#include<bits/stdc++.h>

using namespace std;

int main(){
  long long x;
  cin >> x;
  vector<long long> res={0};
  for(int d=0;d<60;d++){
    if(x&(1ll<<d)){
      int sz=res.size();
      for(int i=0;i<sz;i++){
        res.push_back(res[i]|(1ll<<d));
      }
    }
  }
  sort(res.begin(),res.end());
  for(auto &nx : res){cout << nx << "\n";}
  return 0;
}

Bonus: 実装例 \(17\) 行目 ( sort ) を丸ごと削除してもこのコードは AC します。理由を考えてみましょう。

解法2. bit 全探索の要領で構成する

はじめに、 \(N\) のうち何 bit 目が \(1\) であるかを列挙しておきます。 ( 列挙した数列を \(A=(A_1,A_2,\dots,A_k)\) とします )
その後、 \(1\) である bit の数を \(k\) として \(0\) から \(2^k-1\) までの整数 \(i\) でループを回します。
このとき、 \(i\)\(j\) bit 目が \(1\) なら \(A_j\) bit 目を立てるようにすると、目的の集合を得ることができます。
実装例(C++):

#include<bits/stdc++.h>

using namespace std;

int main(){
  long long n;
  cin >> n;
  vector<int> a;
  for(int i=0;i<60;i++){
    if(n&(1ll<<i)){a.push_back(i);}
  }
  int k=a.size();
  vector<long long> res;
  for(int i=0;i<(1<<k);i++){
    long long cur=0;
    for(int j=0;j<k;j++){
      if(i&(1<<j)){cur|=(1ll<<a[j]);}
    }
    res.push_back(cur);
  }
  sort(res.begin(),res.end());
  for(auto &nx : res){cout << nx << "\n";}
  return 0;
}

Bonus: 実装例 \(21\) 行目 ( sort ) を丸ごと削除してもこのコードは AC します。理由を考えてみましょう。

解法3. ループを上手く回す

この問題を bit 列で集合を扱う問題に言い換えると、ある集合の部分集合全体を渡る問題になります。実装例のようにループを回せばこの問題に正解できます。
(部分集合を値の大きい方から渡るので、出力する際に逆順にする必要があることに注意してください。)
以下の日本語の記事には、他にも bit 列で集合を扱う際のテクニックが掲載されています。
ビット列による部分集合表現 【ビット演算テクニック Advent Calendar 2016 1日目】
実装例(C++):

#include<bits/stdc++.h>

using namespace std;

int main(){
  long long x;
  cin >> x;
  vector<long long> res;
  for(long long i=(1ll<<60)-1; i>=0; i--){
    i&=x;
    res.push_back(i);
  }
  reverse(res.begin(),res.end());
  for(auto &nx : res){cout << nx << "\n";}
  return 0;
}

posted:
last update: