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
- 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.
- 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.
- 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.
- 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.
- 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.
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';
}