ALIENOR - Editorial

PROBLEM LINK:

Practice
Contest: Division 1
Contest: Division 2
Contest: Division 3
Contest: Division 4

Author: akash_chauhan0
Tester: raysh07
Editorialist: iceknight1093

DIFFICULTY:

TBD

PREREQUISITES:

None

PROBLEM:

You’re given N binary strings, each of length K, denoting the binary representations of a set of integers.

Let A_i denote the i'th integer.
For each x from 0 to 2^{K-1}, does x appear as the bitwise OR of some subset of the A_i?

EXPLANATION:

We’re interested in the integers from 1 to 2^{K-1}, which equivalently is all integers with at most K digits in binary.

Notice that 2^K can be extremely large, so it’s not really possible to check every subset.
Instead, observe that if we have a subset S_1 whose bitwise OR is x, and a subset S_2 whose bitwise OR is y, then the subset S_1\cup S_2 has a bitwise OR of (x\mid y).

Why?

The bitwise OR of a set of strings S is, quite simply, the string that contains a 1 at any position that at least one string from S contains a 1 in.

So,

  • x contains information about all the set positions of S_1.
  • y contains information about all the set positions of S_2.
  • x\mid y contains information about all the set positions of S_1 or S_2, which is just S_1 \cup S_2.

In simpler words, we can build “big” bitwise ORs using “small” ones.
So, intuitively, if we have enough “small” values, we’ll be able to get every value by taking unions like this to get larger ones.

The question is, what exactly is ‘small’ here?
To answer that, think of the “building blocks” of bitwise OR: the numbers which cannot be obtained as the bitwise OR of any other numbers.
These are exactly those numbers that have exactly one bit set in their binary representation, i.e, numbers of the form 00...010...00.

For example, if K = 4, you’re looking at 0001, 0010, 0100, 1000.
If any one of these is absent, it clearly cannot ever be obtained as a bitwise OR of other binary strings.
On the other hand, if they’re all present, we can use the subset union method mentioned above to get any bitwise OR, just by taking the ones corresponding to the set bits.

So, quite simply, we only need to check if every possible string with exactly one bit set is present among the ones we have.
That is, for each i from 1 to K, the string that’s filled with 0's except at position i, should be present in our set.

There are K such strings, and for each of them, you can check each of the N strings we have for its existence - the constraints are small enough that this brute-force check will be fast enough.
There are faster implementations, but they aren’t needed to get AC.

TIME COMPLEXITY:

\mathcal{O}(NK^2) or \mathcal{O}(NK) per testcase.

CODE:

Author's code (C++)
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
using namespace std;

bool canBeImpressed(vector<string> S, int K)
{
    // to mark up each of i belongs to [0,K-1]
    // iff there exists a binary string '1' followed by 'i' zeroes
    map<int, int> pow;

    // CONCEPT - 2^n (in Decimal) =  1 followed by 'n' zeroes (in Binary)
    for (string bin : S)
    {
        // if it's of the form 2^i, then it must only consists of one '1'
        if (count(bin.begin(), bin.end(), '1') == 1)
        {
            // simple math to extract position of '1' from LHS(Unit Place)
            int i = (bin.size() - bin.find('1') - 1);
            pow[i]++;
        }
    }

    for (int i = 0; i < K; i++)
    {
        // if any of i missing, then it can't possible to form subsets for every j belongs [0,2^k-1]
        // because 2^i can't be represent using any subsequence of diff powers of 2's
        if (pow.find(i) == pow.end())
            return false;
    }
    return true;
}

int main()
{
    // INUITION :: 
    // Since we 're required to form subsets for j belongs to [0,2^k-1]
    // We must require k binary string : {2^0,2^1,2^2,..,2^(k-1)}
    // as they can't be formed using Bitwise OR any other subsets 
    // Now, if we observe closely we don't require any more binary strings because
    // We can form every binary string using subset combination of these k binary 
    // strings such that  Bitwise OR = [0,2^k-1]
    int T;
    cin >> T;
    while (T--)
    {
        int N, K;
        cin >> N >> K;
        vector<string> S(N);
        for (int i = 0; i < N; i++)
            cin >> S[i];
            
        if (canBeImpressed(S, K))
            cout << "YES" << endl;
        else
            cout << "NO" << endl;
    }
    return 0;
}
Tester's code (C++)
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF (int)1e18
#define f first
#define s second

mt19937_64 RNG(chrono::steady_clock::now().time_since_epoch().count());

void Solve() 
{
    int n, k; cin >> n >> k;
    
    vector <bool> found(k, false);
    for (int i = 0; i < n; i++){
        string str; cin >> str;
        
        int cnt = 0;
        for (auto x : str) cnt += x == '1';
        
        if (cnt == 1){
            int pos = -1;
            for (int j = 0; j < k; j++) if (str[j] == '1') pos = j;
            
            found[pos] = true;
        }
    }
    
    for (auto x : found) if (!x){
        cout << "No\n";
        return;
    }
    cout << "Yes\n";
}

int32_t main() 
{
    auto begin = std::chrono::high_resolution_clock::now();
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    int t = 1;
    // freopen("in",  "r", stdin);
    // freopen("out", "w", stdout);
    
    cin >> t;
    for(int i = 1; i <= t; i++) 
    {
        //cout << "Case #" << i << ": ";
        Solve();
    }
    auto end = std::chrono::high_resolution_clock::now();
    auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin);
    cerr << "Time measured: " << elapsed.count() * 1e-9 << " seconds.\n"; 
    return 0;
}
Editorialist's code (Python)
for _ in range(int(input())):
    n, k = map(int, input().split())
    strs = []
    for i in range(n):
        strs.append(input().strip())
    have = 0
    for i in range(k):
        for s in strs:
            if s.count('1') == 1 and s[i] == '1':
                have += 1
                break
    print('Yes' if have == k else 'No')
1 Like

But isn’t the statement " If any one of these is absent, it clearly cannot ever be obtained as a bitwise OR of other binary strings" wrong?

Consider K =4 as given in the problem. Let’s say 0001 is not present initially.
But we do have 1111 and 1110. Then the bitwise XOR of these two will produce 0001.

We need to use bitwise OR, not XOR

Thanks. Idk why I was reading it as XOR until now.

2 Likes

in this question of alien or
my approach was to find 2^i (0<=i<k) in n given strings
if i am able to find all the powers of 2 till 2^k then ans is yes otherwise no
but only the last test case is not accepted and i’m not able to figure the test case that where i can go wrong

Your approach would’ve been ok on Python, but not in C++.

According to your last submission:
Solution: 1058769611 (codechef.com)

You are forcing the cast from binary to an signed int. That means, that the maximum number you can store without overflowing is 2^31. However, the constrains say that the N and K can be up to 100. This means, there’s a test case in which you will try to cast a 2^100 number. You can not do that even with a long long.

Using decimal representation is not a good idea.

2 Likes