Official

E - Keys Editorial by en_translator


By the constraints, the number of keys is as small as \(N \le 15\). Also, the tests are also not so many: \(M \le 100\).
Moreover, as described in the problem statement, there are \(2^N\) combinations of authenticities of the keys.

We may perform the \(M\) tests on all the \(2^N\) combinations and count the number of the proper combinations.
Here, \(2^N \times M \le 3.3 \times 10^6\) tests are required. Even if you perform \(N\) operations for a test, the total number of operations is bounded by \(5 \times 10^7\), which is fast enough to be accepted for this problem.

In order to brute-force over the \(2^N\) combinations, bitwise enumeration.

For example, use a for loop that iterates over \(i=0,1,\dots,2^N-1\). If the \(2^k\)s place of \(i\) is \(0\), we assume that the \((k+1)\)-th key is a dummy; if it is \(1\), we assume it a real one. This way, we can exhaustively enumerate all the combinations.

For more details on bitwise enumerations, please refer to the following articles (all in Japanese).

Note that spending \(N^2\) operations per test may not fit in the execution time limit.
For example, manage whether the \(k\)-th key was used, just as in the manner of packet sort, to limit the number of operations for a test within \(N\).

Sample code (C++):

#include<bits/stdc++.h>

using namespace std;

int main(){
  int n,m,k;
  cin >> n >> m >> k;
  vector<vector<int>> key(m,vector<int>(n,0));
  vector<string> r(m);
  for(int i=0;i<m;i++){
    int c;
    cin >> c;
    for(int j=0;j<c;j++){
      int a;
      cin >> a;
      key[i][a-1]=1;
    }
    cin >> r[i];
  }

  int res=0;
  for(int i=0;i<(1<<n);i++){
    vector<int> tf(n);
    for(int j=0;j<n;j++){
      if(i&(1<<j)){tf[j]=1;}
      else{tf[j]=0;}
    }
    bool jud=true;
    for(int j=0;j<m;j++){
      int ck=0;
      for(int p=0;p<n;p++){
        if(key[j][p]==1 && tf[p]==1){ck++;}
      }
      if(ck>=k && r[j]=="x"){jud=false;}
      if(ck<k && r[j]=="o"){jud=false;}
    }
    if(jud){res++;}
  }
  cout << res << "\n";
  return 0;
}

One can use bitwise operations as follows.
This runs faster than the sample code above.

Sample code (C++):

#include<bits/stdc++.h>

using namespace std;

int main(){
  int n,m,k;
  cin >> n >> m >> k;
  vector<int> ks(m,0);
  vector<int> r(m,0);
  for(int i=0;i<m;i++){
    int c;
    cin >> c;
    for(int j=0;j<c;j++){
      int a;
      cin >> a;
      a--;
      ks[i]|=(1<<a);
    }
    string s;
    cin >> s;
    if(s=="o"){r[i]=1;}
    else{r[i]=0;}
  }

  int res=0;
  for(int i=0;i<(1<<n);i++){
    bool jud=true;
    for(int j=0;j<m;j++){
      int ok=__builtin_popcount(i&ks[j]);
      if(ok>=k && r[j]==0){jud=false; break;}
      if(ok<k && r[j]==1){jud=false; break;}
    }
    if(jud){res++;}
  }
  cout << res << "\n";
  return 0;
}

posted:
last update: