Блог пользователя BledDest

Автор BledDest, 3 дня назад, перевод, По-русски

2283A - Числа на доске

Разбор
Решение

2283B - Ритмичная строка

Разбор
Решение

2283C - Большой XOR

Разбор
Решение

2283D - Чёрный, серый и белый

Разбор
Решение (awoo)

2283E - Разбиение бинарной строки

Разбор
Решение

2283F - Минимизация графа

Разбор
Решение

2283G - Статистика на графе

Разбор
Решение
  • Проголосовать: нравится
  • +60
  • Проголосовать: не нравится

»
3 дня назад, скрыть # |
← Rev. 4  
Проголосовать: нравится +1 Проголосовать: не нравится

G can be solved with simple brute force over all 2^N masks + binary lifting lol

Code

and an alternative approach for C would be, we just need to choose which numbers from 1 to N to double so that the XOR of the resulting array is at least twice the bitwise OR of the original array

The key observation is that we only need to double powers of two. Their effects on the XOR allow us to construct the exact target value

Code
  • »
    »
    3 дня назад, скрыть # ^ |
     
    Проголосовать: нравится 0 Проголосовать: не нравится

    The brute force in-question,

    Although main thing is that even though binary lifting was one of the intended solutions actually passing it under the time contraint is difficult.

»
3 дня назад, скрыть # |
 
Проголосовать: нравится +3 Проголосовать: не нравится

thanks for the solutions sir!

»
3 дня назад, скрыть # |
 
Проголосовать: нравится +3 Проголосовать: не нравится

F can be solved just using lazy segment trees 397403664.

»
3 дня назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

I had nearly an identical solution to E in my head lmao.

»
3 дня назад, скрыть # |
 
Проголосовать: нравится +1 Проголосовать: не нравится

Combination of 2 distinct dps on E was amazing!

»
3 дня назад, скрыть # |
← Rev. 2  
Проголосовать: нравится 0 Проголосовать: не нравится

Hello! My approach for problem C was as follows:

First, calculate 2 * OR of all elements. We now need to set the MSB (Most Significant Bit) in our XOR, which is present in 2 * OR. We also know that if the previous MSB of the OR was at position (x), it will now be at (x + 1) (since multiplying by 2 left-shifts all set bits). Therefore, we can double the (N)-th element since it has the largest MSB, left-shifting it sets the MSB of 2 * OR in our XOR. Let (X = 2 * OR). We iterate from the highest bits downward. If a bit is set in (X) but not in our XOR, then obv we have will try to Set it cuz even if we set all the smaller bits still X will be > Since Sum of all bits from 0..i (2 ^ 0 + 2 ^ 1.. 2 ^ i < 2 ^ (i + 1) )Doubling an element equal to 1LL << (i — 1) (2 ^ (i — 1)) left-shifts its bit, which sets that bit in our XOR, so we update the XOR accordingly.

Here is my solution: 397390036

Code:

The main reason I'm posting this is to ask: is my code hackable? I mistakenly wrote Ans[N — 1] = 1 instead of Ans[N] = 1 (I accidentally mixed up 0-based and 1-based indexing) and it got AC during contest.

»
3 дня назад, скрыть # |
 
Проголосовать: нравится +2 Проголосовать: не нравится

I have a greedy approach for Problem C.

Greedy: Process numbers from large to small

  1. Key observation The bitwise OR of all integers from 1 to n is always a number consisting entirely of consecutive 1-bits. Let L be the highest set bit of n. Then k = (1 << (L + 1)) — 1, and 2*k has all bits from position 1 to L+1 set to 1 (using 0-based indexing). Also, every chosen value is at most 2*n, so the final XOR is smaller than 2^(L+2). Therefore, we only need to make bits 1 through L+1 of the final XOR equal to 1. The result will automatically be either 2*k or 2*k+1.

  2. Greedy strategy Process i from n down to 1, maintaining tmp, the XOR of all processed elements. For each i:

  • If replacing i with 2*i makes tmp larger, choose 2*i.
  • Otherwise, keep i unchanged.

In code:

if ((tmp ^ (2 * i)) > tmp) {
    p[i] = 1;
    tmp ^= (2 * i);
} else {
    tmp ^= i;
}
  1. Why does this work? Group the numbers according to their highest set bit. For a group containing numbers in the range [2^q, 2^(q+1)-1], doubling any number in this group makes its highest set bit exactly q+1. The comparison tmp ^ (2*i) > tmp has a simple meaning:
  • If bit q+1 of tmp is 0, doubling i makes the XOR larger, so we double it and turn this bit into 1.
  • If bit q+1 is already 1, doubling i makes the XOR smaller, so we keep i unchanged. Since we process numbers in descending order, the first number in each group will set bit q+1 to 1 if necessary. All remaining numbers in that group are kept unchanged, so they cannot affect this bit. After finishing a group, its corresponding bit is guaranteed to be 1. Moreover, all numbers in subsequent groups are smaller. Neither their original values nor their doubled values can affect any previously fixed higher bit. Thus, we can greedily fix all required bits from high to low, guaranteeing that the final XOR is at least 2*k.

Implementation:

void solve() {
    int n;
    cin >> n;

    vector<int> p(n + 1, 0);
    long long tmp = 0;

    for (int i = n; i >= 1; --i) {
        long long x = 2LL * i;

        if ((tmp ^ x) > tmp) {
            p[i] = 1;
            tmp ^= x;
        } else {
            tmp ^= i;
        }
    }

    for (int i = 1; i <= n; ++i) {
        cout << p[i] << " \n"[i == n];
    }
}

Here, p[i] = 1 means choosing 2*i; otherwise, we choose i.

»
3 дня назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

Come on, people were complaining how C was harder than D. I clearly disagree, you needed to know how to binary lift and rip an LCA to solve D effectively.

For C, as long as you knew how to type ^, then you could solve C.

»
3 дня назад, скрыть # |
← Rev. 4  
Проголосовать: нравится 0 Проголосовать: не нравится

Another implementation of C is to greedily multiply the numbers, starting with the largest.

void solve(){
	int n, ans = 0;
	cin >> n;
	vector<bool> a(n + 1);
	
	for(int i = 1; i <= n; i++) ans ^= i;

	for(int i = n; i > 0; i--) if(((ans ^ i) ^ (2 * i)) > ans) a[i] = 1, ans = (ans ^ i) ^ (2 * i);
	
	for(int i = 1; i <= n; i++) cout << a[i] << " ";
    cout << "\n";
	
}
»
3 дня назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

Wow, noticed the std for A is Python :). But I think maybe the last line print(max(0, r - l - 2 * m)) is missing a - 1 according to the tutorial formula. Is this a new anti-plagiarism feature, or just a cute little typo? ovo

»
3 дня назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

If you are noob like me, you will make the whole xor basis first then try to maximise xorsum greedily :(

Code : 397392370

»
3 дня назад, скрыть # |
← Rev. 2  
Проголосовать: нравится +3 Проголосовать: не нравится

Damn the editorial approach for D is so much better i used LCA + difference tree (idk the name i just wrote difference array for tree) the algo itself was simple just a lot of preprocessing the color tree thing is way more beautiful

»
3 дня назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

are the educational rounds of codeforces is tougher than usual div 2?

»
3 дня назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

Well, I found myself forget to turn off sync_with_stdio and use endl instead of '\n', leading to two TLE of D :(

»
3 дня назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

I think this round was way too easy compared to the others. I solved 2 problems without a single failure.

»
3 дня назад, скрыть # |
← Rev. 2  
Проголосовать: нравится 0 Проголосовать: не нравится

Solution of problem D using LCA ```````` //This is the code

include <bits/stdc++.h>

using namespace std;

void dfs(vector<vector>&up,vector<vector>&adj,vector&tin,vector&tout, int node,int p,int &timer,int l) { tin[node] = ++timer; up[node][0] = p; for (int i = 1; i <= l; ++i) { up[node][i] = up[up[node][i — 1]][i — 1]; }

for (int u : adj[node])
 {
      if (u != p)
           {
                dfs(up,adj,tin,tout,u,node,timer,l);
           }
 }

 tout[node] = ++timer;
 return;

}

bool is_ancestor(int u, int v,vector&tin,vector&tout) { return tin[u] <= tin[v] && tout[u] >= tout[v]; }

int find_lca(int u, int v,int l,vector<vector>&up,vector&tin,vector&tout) { if (is_ancestor(u, v,tin,tout)) return u; if (is_ancestor(v, u,tin,tout)) return v; for (int i = l; i >= 0; --i) { if (!is_ancestor(up[u][i], v,tin,tout)) u = up[u][i]; } return up[u][0]; }

bool fun(int total,int target,int st,vector<vector>&up,vector&tin,vector&tout,int l) { while(1) { int end=total-st; if(st>=end) { break; }

int lca=find_lca(st,end,l,up,tin,tout);

       int l1=find_lca(target,st,l,up,tin,tout),l2=find_lca(target,lca,l,up,tin,tout);
       if(l1==target&&l2==lca)
       {
           return true;
       }

       int l3=find_lca(target,end,l,up,tin,tout);
       if(l3==target&&l2==lca)
       {
           return true;
       }

       st++;
    }

    return false;

}

signed main() { int t;cin>>t; while(t--) { int n;cin>>n; vector<vector>adj(n+1); for(int i=1;i<n;i++) { int u,v;cin>>u>>v; adj[u].push_back(v); adj[v].push_back(u); }

if(n%2==0)
{
    cout<<n+1<<endl;
}
else 
{ 
    int l=ceil(log2(n));
    vector<vector<int>>up(n+1,vector<int>(l+1,0));
    vector<int>tin(n+1,0),tout(n+1,0);
    int timer=0;

    dfs(up,adj,tin,tout,1,1,timer,l);

    int st=1,a=1,st1=2;

    if(fun(n+1,(n+1)/2,st,up,tin,tout,l))
    {
      cout<<n+1<<endl;
    }
    else if(fun(n+2,a,st1,up,tin,tout,l))
    {
       cout<<n+2<<endl;
    }
    else if(fun(n,n,st,up,tin,tout,l))
    {
      cout<<n<<endl;
    }
    else 
    {
      cout<<n+1<<endl;
    }
}

}

} ```````````` // This is solution using LCA .

»
3 дня назад, скрыть # |
 
Проголосовать: нравится +1 Проголосовать: не нравится

thanks for the lightning-fast editorial chef!

»
3 дня назад, скрыть # |
← Rev. 4  
Проголосовать: нравится -11 Проголосовать: не нравится

[DELETED]

By mistake, wrong question's ans was posted

»
3 дня назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

I think there is an mistake in the Problem C's editorial the code doesnt correctly set the d+1 bit to 1 it only starts at d. (i tried to submit the same code and got wrong answer on test 1)

»
3 дня назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

I think e was amazing and intuitive but it was just a slight bit easy for a D2E :)

»
3 дня назад, скрыть # |
 
Проголосовать: нравится -10 Проголосовать: не нравится

This solution runs in $$$O(2 ^ n (n+\log{t})+q \cdot n \log{t})$$$ time. It is very difficult to fit into the time limit.

My solution with this time complexity takes only 1.3/7 seconds: 397513584

At the same time, my unoptimised solution takes 9 seconds. This is an example of terrible problem preparation, as people shouldn't need to think about cache effects to get their code to AC.

»
2 дня назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

The implementation of c seems wrong to me there are left over debug code, and also while while((k >> d) & 1) d++; doesnt seems to give msb please check it thanks.

»
2 дня назад, скрыть # |
← Rev. 5  
Проголосовать: нравится 0 Проголосовать: не нравится

pascal number with different transition sol for E:

dp[len][number of set] = ((number of set)*dp[len-1][number of set]) + dp[len-1][(number of set)-1]

to get answer for certain len you sum (1 <= number of set <= len) which can also be precomputed

»
42 часа назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

397492841

My solution for C based the solution around the fact that xor of a and a<<1 has always even number of set bits. From here on we check for 2 numbers namely a and b, and test it based on the above fact. Reverse engineering the solution suppose some numbers from the set a1, a2...an were right shifted, we can xor and additional 0 which is equal to p^p where p= xor of all the numbers which were xored hence our m is now equal to xor of all numbers from 1 to N ^ (xor of the selected numbers ^ xor of the selected numbers right shifted) now the xor of all numbers can be taken out in O(1) with some cyclic property observation. NOW lets do a bit of rearrangements m^(xor of all numbers from 1 to N)=(xor of the selected numbers ^ xor of the selected numbers right shifted) Using our previously establisehed fact, we now have to check for the values a and b; ie which one will yield us a xor value with even number of set bits. from this result we can backtrack to a number which when xored with its right shifted value, will give us either of a and b. This backtracked number can be constructed from powers of 2 ;)

quite a messsy explanation, sorry peeps!

»
40 часов назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

Problem D soln(different way and O(N) soln) :-

An Elegant O(N) Approach Using Branch Labeling (Subtree Tracking)

I wanted to share my approach for this problem because I found a way to solve it optimally in O(N) time by smartly choosing tree roots and tracking which branch every node belongs to.

Here is the breakdown of the logic:

1. The Base Case (Even N) If N is even, the answer is always simply N + 1. No traversal is needed.

2. The Odd Case (Testing Candidates) If N is odd, the answer will be one of three candidates: N, N + 1, or N + 2. Instead of doing something complex, we can just test these three candidate values of K in ascending order. Since we want to update our answer if a valid state is found, looping K from N to N + 2 naturally leaves us with the highest valid candidate.

For each candidate K, we pick a very specific root to hang the tree from: * If K = N, root = N * If K = N + 1, root = (N / 2) + 1 * If K = N + 2, root = 1

3. The "Second Parent" (Branch Labeling) Technique Once we select the root for our candidate K, we need to check if there is a valid pair of nodes (i, target) where target = K — i, such that they lie in different branches connected to the root.

To do this efficiently, I used a second_parent array: * I iterate through all immediate children of the root. * For each child, I run a DFS to traverse its entire subtree. * During this DFS, every node in that subtree is tagged with that specific child as its second_parent (essentially labeling which main branch of the root it belongs to).

4. The Validation Step After the tree is fully labeled, validating the candidate K takes just O(N) time: * Iterate through all possible nodes i from 1 to N. * Calculate the required pair: target = K - i. * If the target is valid (1 <= target <= N and target != i), we just check their labels: if (second_parent[i] != second_parent[target]). * If they have different branch labels, they don't share the same immediate branch from the root, meaning we found a valid pair for this K! We update our answer me = K.

Complexity: Because we only test 3 candidates when N is odd, we run a standard DFS and a simple linear scan 3 times. * Time Complexity: O(N) * Space/Memory: O(N) for the adjacency list and the second_parent array.

You can check out my implementation here: Submission #397399210

Hope this helps anyone looking for a clean linear-time solution! Let me know if you have any questions about the routing logic.