cppAcademy
FROM LEARNING TO COMPETING

Expert.
Put your skills to the test.

Bulgarian Informatics Group A · 2007–2026

274 independently explained problems: 54 Municipal, 54 Regional, 109 National Olympiad and 57 Autumn Tournament tasks. Difficulty labels are learning estimates, not official ratings. Some events share Senior/IATI or Group A/B problem sets. Special formats, scoring limitations and source clarifications are explained on affected tasks. Existing shared National-round problems keep their permanent URLs.
GROUP A2020 · HardMeet in the middle and fixed-weight bitmasks

Count Alphabet Assignments from Names

Autumn Tournament (2020)

Submission name: alpha.cpp

Problem summary

There are26 names, one for each student in order. Choose one distinct letter from each name so that every uppercase English letter is chosen exactly once. Count the resulting alphabet permutations; repeated occurrences of a letter within one name do not create extra choices.

Source clarification. The fixed-weight mask compression and memory bound follow the official editorial: https://infos.infosbg.com/files/Contests/ETI/2020/A/analyses/alpha_analysis.pdf

Input

26 uppercase names, separated by whitespace.

Output

The number of valid alphabet permutations.

Constraints

Each name has length1…5; exactly26 names; the answer is at most10^18.

Worked example

Original statement sample.

Input

ELLY KRISS STAN WU ALEX ZOE POOH
KATE VILI MAXIM QT G ZAEK BOBI TEO
YOYO DEQN FIL CECO DAWG JORO MONI
SASHO DUDA VESKO POOH

Output

1592

Solution analysis

  1. Represent each name by a26-bit mask. A repeated L in ELLY sets the same bit twice, so it is naturally counted only once. During a search, options=nameMask&~usedMask selects exactly the still-unused letters available to that student.
  2. Split the26 students into two halves of13. Enumerate all valid assignments in the first half and count how many produce each13-letter mask. Enumerate assignments in the second half; a second-half mask can combine only with its exact complement among the26 letters. Add the stored count for that complement.
  3. A direct array for all26-bit masks would be too large. Every stored mask has exactly13 set bits. Removing its lowest bit by shifting right is injective within this fixed-weight family: if two masks become equal after the shift, they differ only at bit0, which would change the number of set bits. Thus an array of2^25 int counters is sufficient.
  4. One counter fits in int because a half has at most5^13=1220703125 assignments. The final total needs long long. The search visits only branches with unused available letters; many branches terminate before depth13.
  5. For a transparent small analogue, names AB, AB, C, D have two complete assignments: ABCD and BACD. If the first half uses{A,B}, the second must use its complement{C,D}. The official26-name sample applies the same combination rule and yields1592.

Why it works

Each full valid assignment restricts uniquely to two valid13-student assignments with complementary masks. Conversely complementary masks are disjoint and jointly contain all26 letters, so combining either half assignment yields a valid full permutation. Multiplying their multiplicities and summing over complements therefore counts every valid output once. The fixed-popcount argument proves that compressed array indices never merge distinct stored masks.

Complexity: O(5^13) worst-case search work up to a constant factor for the two halves, with substantial pruning when letters repeat across names. O(2^25) integer storage (128MiB), plus recursion depth13. This is the intended memory-efficient meet-in-the-middle solution, not a2^26 long-long DP.

Watch out. Deduplicate letters inside a name. Reset the used mask between halves; do not reuse the first half mask as the starting state for the second search. Complement only the low26 bits. The shifted-mask compression is valid only because every terminal mask has exactly13 ones.

Commented C++17 solution

An independently written complete program with standard headers.

#include<array>
#include<cstdint>
#include<iostream>
#include<string>
#include<vector>
using namespace std;
array<uint32_t,26>allowed{};
vector<int>counts;
long long answer=0;
void enumerate(int position,int offset,uint32_t used){
    if(position==13){
        if(offset==0){
            counts[used>>1]++;
        }
        else{
            uint32_t complement=((1u<<26)-1)^used;
            answer+=counts[complement>>1];
        }
        return;
    }
    uint32_t options=allowed[offset+position]&~used;
    while(options!=0){
        uint32_t bit=options&-options;
        options^=bit;
        enumerate(position+1,offset,used|bit);
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    for(uint32_t&mask:allowed){
        string name;
        cin>>name;
        for(char letter:name){
            mask|=1u<<(letter-'A');
        }
    }
    // Terminal masks have a fixed popcount, so dropping one bit is injective.
    counts.assign(1u<<25,0);
    enumerate(0,0,0);
    enumerate(0,13,0);
    cout<<answer<<'\n';
}

Source: Arena Olimpiici ↗