There isn't anything particularly interesting or difficult about this problem, writing this blog is just a way for me to regain a general coherence in my approach to solving problems.
Problem CF-2056F1
Statement
A sequence
of integers is called good iff the following condition holds:
- Let
be the number of occurrences of in the sequence . - For all pairs
, at least one of the following must be true:
In other words, if both
and are present in the sequence, then the number of occurrences of must be less than or equal to the number of occurrences of . You are given integers
and . Compute the bitwise XOR of the medians of all good sequences of length , where each element satisfies .
Constraints:
(given to us in its binary form)
Solution
A reasonable (and probably the only immediately obvious) direction to take is to fix the median and then consider the set of good sequences that correspond to that median, as
Clearly, for a fixed median
- Iterate over
. - Find the parity of the number of good sequences with
as their median. - If the aforementioned value is odd, include
within the final set of terms to be XORed to get the answer.
Let's define two functions for convenience:
: The number of good sequences with as their median. .
A very brief consideration should reveal to one the huge difference in computational difficulty between the analysis of
Let’s partition all sequences with median
Now, it's not difficult to see this expression and guess that it's a bit difficult to get it to be odd (ie. it is even for most partitions of
Let
For the aforementioned expression to be odd, we require
When does a partition
of an integer have ?
Let's focus on
It shouldn't seem too unmotivated of a directional change when we now consider the binary representations of the numbers involved here (division by 2 is a right shift, we're dealing with huge numbers, etc.): We reinterpret the expression described before by considering the contribution of each bit in the binary representation of
The comparison of
We finally have a result we can use:
(where all elements in multiset sum to ) is odd No two elements of have a common set bit
This implies that any "good" additive partition
We now make the following key observation:
There is a unique maximum element in
, and it is (since the most significant bit of is set in exactly one element of ).
Let us return to the problem at hand. To recap, we:
- Fixed median
. - Fixed a multiset
with median and analyzed all sequences that correspond to this multiset.
Now, how does our result help us here? Well:
- We now only care about multisets whose "frequency set" constitutes a good partition of
(We will call a multiset good if it's frequency set is good). - We're relieved of the responsibility of having to force
to be the median, since exactly one element has a frequency in a good multiset and is indisputably the median. This element must obviously be .
For a fixed
How do we find the parity of the number of good multisets with median
This is a very simple sub-problem that can be solved with the help of dynamic programming:
- Define
to be the parity of the number of ways to partition a sequence of length into non-empty subsequences. Compute all for (where is the number of set bits in ) in (using the transition ). - Then, we iterate over the number of unique elements,
, in our good multiset. The contribution of the number of good multisets with unique elements shall be . We now apply our previously derived result again and realize that the parity of is 1 if and only if (where is the bitwise-XOR operator), and add the parity of this expression to our answer.
We therefore compute this parity for every median
We can however, make our solution faster by simply precomputing the dynamic programming states, which brings the time complexity of our solution down to
The code ends up being rather simple, and has been included below.
#include<bits/stdc++.h>
using namespace std;
const int K = 205;
int dp[K][K];
signed main()
{
ios_base::sync_with_stdio(false), cin.tie(NULL);
dp[0][0] = 1;
for(int i = 1; i < K; i ++)
{
for(int j = 0; j < i; j ++)
dp[i][j + 1] += dp[i - 1][j], dp[i][j] += dp[i - 1][j] * j;
for(int j = 0; j <= i; j ++)
dp[i][j] &= 1;
}
int t = 1;
cin >> t;
while(t --)
{
int k, m;
cin >> k >> m;
vector<char> n(k);
for(int i = k - 1; i >= 0; i --)
cin >> n[i];
int b = count(n.begin(), n.end(), '1');
int ans = 0;
for(int med = 0; med < m; med ++)
{
int cnt = 0;
for(int x = 1; x <= min(b, med + 1); x ++)
if(((x - 1) ^ (med - (x - 1))) == med)
cnt += dp[b][x];
ans ^= ((cnt & 1) * med);
}
cout << ans << endl;
}
}