BledDest's blog

By BledDest, history, 2 months ago, translation, In English

2242A - Bigrams

Idea: BledDest

Tutorial
Solution (BledDest)

2242B - Predominant Frequency Division

Idea: FelixArg

Tutorial
Solution (FelixArg)

2242C - Unstable Elements

Idea: BledDest

Tutorial
Solution (BledDest)

2242D - Two Digit Strings

Idea: Roms

Tutorial
Solution (BledDest)

2242E - Product of Closures

Idea: adedalic

Tutorial
Solution (adedalic)

2242F - Summer Vacation

Idea: FelixArg

Tutorial
Solution (FelixArg)
  • Vote: I like it
  • +109
  • Vote: I do not like it

»
2 months ago, hide # |
Rev. 2  
Vote: I like it 0 Vote: I do not like it

How cooked am I if my first thought to solving B is range update range query

Spoiler
Or
»
2 months ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

How to solve C if the array is not sorted?

  • »
    »
    2 months ago, hide # ^ |
     
    Vote: I like it 0 Vote: I do not like it

    Technically the array doesn't need to be sorted, since the condition was for a[i] != a[i-1], not a[i] > a[i-1], meaning that same numbers had to be grouped next to each other. Something like 1 1 1 5 5 3 3 3 2 2 9 9 7 7 7, which you can easily count the number of marked elements for.

    For arrays in which same numbers aren't grouped together, for example like 1 3 5 2 3 1 4 3 1 5, you can just treat every element as a different group

    • »
      »
      »
      2 months ago, hide # ^ |
       
      Vote: I like it 0 Vote: I do not like it

      Thanks bro

    • »
      »
      »
      2 months ago, hide # ^ |
      Rev. 2  
      Vote: I like it +15 Vote: I do not like it

      But in that case you will have to deal with different blocks merging together:

      For example take: 2 2 1 2 2 2

      After 1 delete op: 2 2 2

      After 2 delete op: 2 2

      So being sorted is actually needed for this solution to work.

  • »
    »
    2 months ago, hide # ^ |
     
    Vote: I like it 0 Vote: I do not like it

    perhaps it remain the same,I don't think it needs to change

»
2 months ago, hide # |
 
Vote: I like it +5 Vote: I do not like it

did anyone solve problem F using std::rope? My solution is 7 lines, but unfortunately the relatively low time limit and std::rope having a terrible implementation means it TLEs even with periodic rebuilds. (even O(nsqrt(n)) block decomposition implementation is faster)

code
»
2 months ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

Extremely hard B

  • »
    »
    5 weeks ago, hide # ^ |
     
    Vote: I like it 0 Vote: I do not like it

    so easy;

    #include <bits/stdc++.h>
    using namespace std;
     
    #define ll long long
    #define vii(vi) vector<vector<ll>> vi
    #define vi(v) vector<ll> v
    #define faur(b,i) for(int i = 0; i<b;i++)
    #define MOD 1000000007
     
    int main() {
        int t;
        cin >> t;
        while (t--) {
            int n ;
            cin>> n;
            vector<ll>arr(n);
            
            
            faur(n,i){ cin>>arr[i] ;}
     
            
            int cnt = 0;
            int temp = 0;
           faur(n,i){
            if(cnt == 0){
                
                if(arr[i]!=1){ 
                    temp++ ;
                }
                else if(temp){
                    temp --;
                    if(temp==0){cnt = 1; temp = 0;}
                }
                else{
                    if(i){
                        cnt = 1;
                    }
                    else{
                        if (arr[1]==3){i++ ;}
                        cnt = 1;
                        
                    }
                }
            
            }
            else if(cnt == 1){
            
                if(arr[i]==3){ 
                    temp++ ;
                }
                else if(temp){
                    temp --;
                    if(temp==0){
                        if(i != n-1) cnt = 2; }
                }
                else{
                    
                    if(i != n-1)    cnt = 2;
                    
                }
     
            }
           
         }
     
         (cnt==2)?cout<<"yes" : cout<<"no" ;
     
         cout<<endl ;
     
        }
         
        return 0;
    }
    
»
2 months ago, hide # |
 
Vote: I like it +3 Vote: I do not like it

D was a good problem though. In contest I did not recognize how to transform such a hard problem into an easy one. Great job, authors.

»
2 months ago, hide # |
Rev. 2  
Vote: I like it +3 Vote: I do not like it

In Problem C, I missed the fact that the array was sorted. I ended up using DSU to connect the gaps, overcomplicating the solution that took more than 60 minutes during the contest. Here's the solution.

»
2 months ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

what a cool solution for D... I honestly thought it was subsegment DP or smth

»
2 months ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

The B was very difficult to solve .

  • »
    »
    2 months ago, hide # ^ |
     
    Vote: I like it 0 Vote: I do not like it

    Are You Kidding?!

    • »
      »
      »
      2 months ago, hide # ^ |
       
      Vote: I like it 0 Vote: I do not like it

      I wish I was! I struggled a bit with the logic/implementation. Do you have any tips or a cleaner approach for it?

      • »
        »
        »
        »
        2 months ago, hide # ^ |
        Rev. 2  
        Vote: I like it 0 Vote: I do not like it

        You bet! You can just solve it with a brute-force approach. First, check whether the left segment is feasible. If it is, start checking the middle segment from the end of the left segment. If you still can't find a valid partition, keep extending the left segment and repeat the process until you either find a valid partition or determine that none exists.I wish my approach can hope you , it is O(n^2)(I think)

»
2 months ago, hide # |
Rev. 4  
Vote: I like it 0 Vote: I do not like it

..

»
2 months ago, hide # |
Rev. 2  
Vote: I like it 0 Vote: I do not like it

I would love to learn from someone if they have approaches for D different from the editorial

  • »
    »
    2 months ago, hide # ^ |
     
    Vote: I like it 0 Vote: I do not like it

    My idea doesn't use prefix sums,

    First, lets consider when the answer is -1. As we can see, each operation doesn't change the sum of digits in a number modulo $$$10$$$.

    Lets follow this idea a little, we consider some consecutive pairs of digits that we will never do an operation on(in both numbers). These pairs separate both digits into "blocks", that, after applying all operations will become single digits. As we can see, the goal in this task, is to separate those 2 numbersinto the same (maximum) number of blocks, where the blocks have the same sums modulo $$$10$$$ (i mean the first block in s1, has the same mod sum as the first block in s2 and so on).

    Let $$$dp(i,j)$$$ be the maximum number of blocks that we can divide the prefix of length $$$i$$$ in $$$s1$$$, and the prefix of length $$$j$$$ in $$$s2$$$, it is easy to calculate this (you would have to consider $$$O(n)$$$ blocks) $$$dp$$$ in $$$O(n^3)$$$ time.

    But fortunately, there is an optimization, consider the index $$$i$$$, and the maximum $$$l$$$ such that $$$l \leq i$$$, and $$$\sum_{j = l}^{i}{s_j} \equiv d \pmod{10} $$$, where $$$d \in$$$ { $$$0,1 \dots, 9$$$ } where $$$s$$$ is either $$$s1$$$ or $$$s2$$$, it doesn't matter for this optimization. let $$$l_1$$$ be the maximum index such that $$$l_1 \leq l$$$, and $$$\sum_{j = l_1}^{i}{s_j}$$$, as you can see, $$$\sum_{j=l_i}^{l-1}{s_j} \equiv 0 \pmod{10}$$$, so we can simply consider the smallest block with the sum, and don't worry about the rest.

    submission : 381524391

    • »
      »
      »
      6 weeks ago, hide # ^ |
      Rev. 2  
      Vote: I like it 0 Vote: I do not like it

      My idea is similar to yours by setting dp(i, j) as x where x is the number of blocks after dividing a[0:i] and b[0:j], given that sum(a[0:j])%10=sum(b[0:j])%10. But getting dp(i, j) is a bit different than yours.

      It's pretty easy to tell that dp(i, j)=max(dp(i2, j2))+1 where i2<i and j2<j, if you try to do that intuitively it will TLE. but we can optimize by using a segment tree.

      Use a segment tree of size j, and segtree[j] is max(dp(i2, j)) for i2<i. We update segtree[j] as max(dp(i, j), segtree[j]) after each iterating through all j for each i. Our final answer will be dp(n-1, m-1). My submission optimized the dp array as 1D but it's probably not necessary.

      submission: 383874331

»
2 months ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

I like how B is rated 881 on clist, because its definitely a 900 level problem. Sure, i mean prefix and suffix arrays were historically 1200 level concepts. But surely theres nothing fishy going on here, right? I guess everyone just got 300 points better

  • »
    »
    2 months ago, hide # ^ |
     
    Vote: I like it 0 Vote: I do not like it

    I solved B without prefix/suffix arrays, with one greedy loop: 381481048

    • »
      »
      »
      2 months ago, hide # ^ |
       
      Vote: I like it 0 Vote: I do not like it

      That's not greedy that's god level process of elimination in coding

      • »
        »
        »
        »
        2 months ago, hide # ^ |
         
        Vote: I like it 0 Vote: I do not like it

        I also did using greedy but way simpler. Ideas was that for the first block, if you have excess ones for some odd length segment, its better to include the next element in the segment if it is a 3. In general this is a greedy problem only in my opinion. As you only mostly need to construct the first segment rightfully. 2nd and 3rd segment constructions are quite trivial. I initially was constructing first segment by only checking if the number of ones are >= len/2 but it failed on example test case 9. Then I came up with the include 3 logic which was a quick fix to my inital greedy solution. Although yes I do agree with you, this is definitely not a 900 rated problem. Neither is C a 1260 rated problem as shown in CList. I have done many problems and I am pretty sure B is at least 1100-1200 and C is 1400-1500. Here is my submission for B btw:

        381477898

»
2 months ago, hide # |
 
Vote: I like it +8 Vote: I do not like it

Thanks for the amazing problems, but I like to point out that the problem F constrained with 1.5 second actually lets through a brute force solution with vectorization. Was this intentional? I was shocked to read the problem and finding it easier than B (the wording could have been simpler for B and C).

  • »
    »
    2 months ago, hide # ^ |
    Rev. 2  
    Vote: I like it +5 Vote: I do not like it

    wait how did it get acc?

    Revision: i have submitted 30+ solutions on this problem, and quite i say pragma is very weird idk if im right, but im pretty sure it has something to do with "Cache Locality" where code runs faster if it has more "predictive" requests

    honestly, crazy work. this type of optimization is scarily useful, but i will not try to master this optimization

    • »
      »
      »
      2 months ago, hide # ^ |
       
      Vote: I like it +10 Vote: I do not like it

      I think it doesn't have anything to do with cache locality. The compiler cannot vectorise your loop (Loop Analysis) because of how it is structured, in his code his outer loop is the one iterating over a[i] the inner loop is completely independent (that is for all j = [0 ... i]) it can be done in parallel, the compiler can see this and use Vector Instructions to do it which is fast enough to overcome TLE. Regardless of how you do it (j = [1...i] or j = [0...i]) you will have cache locality because you are accessing elements in a contiguous range.

      • »
        »
        »
        »
        2 months ago, hide # ^ |
        Rev. 2  
        Vote: I like it +6 Vote: I do not like it

        ohhh, so like working in parallel/pipelining. that makes more sense

        vectorization is the handling of predictable instructions to an array/vector that can speed up time complexity. that is easier to think about when i want to save time

        thanks for the PositiveFeedback

        • »
          »
          »
          »
          »
          2 months ago, hide # ^ |
           
          Vote: I like it +5 Vote: I do not like it

          Hi, The flag prediction stalls the pipeline as overhead. Moreover, you may read about programming Branchless Instructions. cppcon yt is great to start with.

»
2 months ago, hide # |
Rev. 4  
Vote: I like it +3 Vote: I do not like it

Problem 2242E - Product of Closures was fun. I couldn't figure out all the edge cases during the contest, but managed to solve it afterwards with a simpler but less efficient solution that runs in $$$\mathcal{O}(n \log^2 r)$$$ per testcase. With up to 1000 cases per test it's kind of pushing it, but apparently it still runs in under 1 second: 381658269

The idea is simple:

solution for problem E

I have no idea if this approach was considered and supposed to pass. The main benefit is that you can find this solution by optimizing the bruteforce solution, without doing any of the complicated casework.

»
2 months ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

How can I copy a subtree (e.g. $$$\color{blue}{4}, \color{blue}{5}, \color{blue}{0}, \color{blue}{1}$$$) and move them to two different positions within feasible time complexity in question F.

»
2 months ago, hide # |
 
Vote: I like it +8 Vote: I do not like it

Solve E in $$$O(Tn\log^2V)$$$ too,and failed on system test.

I heard that there're only 6 weak pretests in problem E,which was misleading.

»
2 months ago, hide # |
Rev. 2  
Vote: I like it 0 Vote: I do not like it

A clean $$$O(N)$$$ approach for 2242C — Unstable Elements

Every operation changes each block's frequency by $$$+1$$$ or $$$-1$$$. If only blocks with initial frequency $$$\ge i$$$ survive, we define $$$s_1$$$ as the number of active blocks, and $$$s_2$$$ as their baseline total size.

When shifting the index down from $$$i$$$ to $$$i-1$$$, each already surviving block grows by $$$1$$$ and we discover $$$c[i-1]$$$ new blocks, giving the natural recurrence $$$s_1^{(i-1)} = s_1^{(i)} + c[i-1]$$$ and $$$s_2^{(i-1)} = s_2^{(i)} + s_1^{(i-1)}$$$. Since subsequent operations expand all $$$s_1$$$ blocks uniformly, $$$k$$$ is reachable if $$$k \ge s_2$$$ and $$$(k - s_2) \pmod{s_1} == 0$$$.

void solve() {
    int n, k;
    cin >> n >> k;
    vector<int> a(n);
    for (int i = 0; i < n; i++) cin >> a[i];
    vector<int> c(n + 1);
    for (int i = 0; i < n;) {
        int j = i;
        while (j < n && a[j] == a[i]) j++;
        c[j - i]++;
        i = j;
    }
    long long ans = 0;
    long long s1 = 0, s2 = 0;
    for (int i = n; i >= 1; i--) {
        s1 += c[i];
        s2 += s1;
        if (c[i] && k >= s2 && (k - s2) % s1 == 0) {
            ans++;
        }
    }
    cout << ans << '\n';
}

Feel free to upvote if you found this helpful!

  • »
    »
    2 months ago, hide # ^ |
     
    Vote: I like it 0 Vote: I do not like it

    such an elegant solution, how did you come up with this approach

    can you suggest some other problems with similar technique

    • »
      »
      »
      2 months ago, hide # ^ |
       
      Vote: I like it 0 Vote: I do not like it

      Thanks! The core idea is just observing the invariant: all blocks with an initial size $$$\ge i$$$ must survive and grow together as a single group.

      By processing the survival threshold in reverse, we avoid complex forward simulation and just maintain a simple linear state.

      For similar techniques, I'd recommend looking into the Contribution to the Sum technique, or problems that use 1D Sweep-line / Frequency Prefix Sums to dynamically maintain global active states.

      • »
        »
        »
        »
        2 months ago, hide # ^ |
         
        Vote: I like it 0 Vote: I do not like it

        Thanks man, never heard of the topic you mentioned but I will definately check them out

»
2 months ago, hide # |
Rev. 3  
Vote: I like it 0 Vote: I do not like it

I think you missed a '}' after 'else cur++;' in solution of C.

Edit: nvm, my bad

»
2 months ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

I solved B using backtracking though i looped through the array finding suitable first partition and for each such half found i would find the second half.

»
2 months ago, hide # |
 
Vote: I like it +3 Vote: I do not like it

'B' could be solved without prefix or suffix arrays, with few observations.

Few observations for B:

  • Part 3 can be any suffix of the array (the condition will be always true, because there is no element greater than 3)

  • Now we only care about Part 1 and Part 2. Part 1 is a the first prefix such that count(1) >= count(2) + count(3). Fix this as Part 1 for now.

  • Now we will start our Part 2, if we encounter either 1 or 2, we can just conclude our part 2. But if we encounter 3, then we can check if its possible to extend our part 1, such that count(1) >= count(2) + count(3) is still true (somewhat like kadane's algo). Also, if we carefully observe, extending part 1, will add only elements '3'. Because, if my part 2 had either '1' or '2' at the left, then there it self condition count(1) + count(2) >= count(3) will be satisfied, and rest will be Part 3.
submission
Follow-up
Spoiler
  • »
    »
    2 months ago, hide # ^ |
     
    Vote: I like it +8 Vote: I do not like it

    I used a similar approach but simplified it even further:

    1. If the array starts with [1, 3], then that is part 1. Otherwise, part 1 is the shortest prefix that satisfies count(1) >= count(2) + count(3).
    2. Part 2 is the next shortest segment that satisfies count(1) + count(2) >= count(3).
    3. Part 3 is the rest of the array. (The only requirement is that the segment is nonempty.)

    The logic behind step 1 is that adding a "bad" element to an existing segment only makes sense if that segment had odd length, which allows us to get rid of a bad element "for free" (i.e. without having to compensate for it with a good element later in the segment). But that only happens when the array starts with 1, otherwise part 1 will have even length.

    Your solution effectively does the same thing, you just have to notice that with only 3 parts, the sum < 0 && prevsum > 0 clause can only trigger when i == 2 and it requires a[0] == 1 && a[1] == 3.

»
2 months ago, hide # |
 
Vote: I like it +3 Vote: I do not like it

For me B was harder than C. Managed to solve C myself after contest, but not B

»
2 months ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

I solved B using a different greedy approach 381515455. I felt this method was easier to prove.Just take two functions: f(arr) = c1 — c2 — c3 and g(arr) = c1 + c2 — c3. Since g() = f() + 2*c2 and c2 cannot be negative, we have g(arr) >= f(arr). Assume a solution exists with partitions P1*, P2*, and P3*. If we choose P1 such that the total number of elements in it has the same parity as P1*, then the extra part, say M, has an even number of elements, so f(M) is also even. We can show that f(M) >= -1. Since it is even, f(M) >= 0. Thus, g(M) >= f(M) >= 0, so g(M + P2*) >= 0. Therefore, if P2* satisfies the property, then M + P2* will also satisfy it.

»
7 weeks ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

is it just me or is D easier than C

»
7 weeks ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

How about Div2?

»
6 weeks ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

The 2242D - Two Digit Strings is pretty interesting. Even using $$$\mathcal O(n)$$$ to find the LCS, I still get WA. I have to to use $$$\mathcal O(n^2)$$$ to pass….

I can prove that my $$$\mathcal O(n)$$$ algorithm is right, so that's pretty interesting you know?

»
11 days ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

388067046 An alternative O(K) time and O(1) space approach for Problem A without sorting or arrays:

Instead of storing frequencies and sorting or tracking top elements, we can accumulate a running sum of only the counts strictly greater than 1 during input streaming:

  • If a count is > 1, its minimum value is 2.
  • The only impossible configurations occur when there are no elements >= 2 (sum = 0) or exactly one element equal to 2 (sum = 2).
  • Any sum >= 3 mathematically guarantees either a single element >= 3 (Type 1 overlap) or at least two elements >= 2 (Type 2 pair).

Thus, the entire check reduces to sum >= 3.