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

Автор Sul_A., история, 11 месяцев назад, По-английски

Editorial for some of the new CSES tasks. Tasks that are trivial or too similar to already existing tasks are not included.

Every time I solve a new nontrivial task, I will add it here.

NOTE: ONLY READ THE SOLUTIONS IF YOU ARE COMPLETELY STUCK AND OUT OF IDEAS. CSES tasks are the kinds of tasks you can solve at a random time by random chance. You might read a task today and solve it next week. Or next month. Or next 3 months. So read at your own risk.

Sorting & Searching

Distinct Values Subsequences

Dynamic Programming

Mountain Range
Minimal Grid Path

Range Queries

Visible Buildings Queries
Range Interval Queries
Distinct Values Queries II

Mathematics

Next Prime
Permutation Rounds

Sliding Window Problems

Sliding Window Minimum
Sliding Window Distinct Values
Sliding Window Mode
Sliding Window Mex
Sliding Window Inversions
Alternative solutions

Interactive Problems

Hidden Permutation
Colored Chairs

Advanced Graph Problems

Fixed Length Walk Queries

Additional Problems

Bubble Sort Rounds I
Counting LCM Arrays
Subsets with Fixed Average
Two Array Average

Additional Problems II

GCD Subsets
  • Проголосовать: нравится
  • +58
  • Проголосовать: не нравится

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

The problem statement of Mountain Range is ambiguous imo. How do you go from mountain a to b when the segments lying in between them are not monotonically decreasing ?
Someone pls explain the problem statement

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

Range Interval Queries can be solved in O(NlogN) offline.

Suppose we have have a function f(i, l, r) that calculates how many elements are there with index <= i and value in [l, r]. Now for each query (a, b, c, d) we add two events:

  • at a-1 remove f(a-1, c, d)

  • at b add f(b, c, d)

f(i, l, r) can be calculated in logN with basic fenwick tree for sum.

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

You can solve Sliding Window Or by maintaining counts of each bit. But my solution required using SIMD intrinsics + pragmas to pass.

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

About the solution of next prime, it's not correct to use average prime gap to estimate complexity since the test cases are not necessarily generated randomly, and the upper bound of prime gap should be used. And I doubt Miller-Rabin test can be run in just one log factor. the implementation from KACTL seems to have 2~3 log factors depending on whether you think size of $$$A$$$ as constant.

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

First problem in the Interactive Problems section requires log n queries instead of n log n. Imo binary search would be much easier to implement in this problem.

  • »
    »
    11 месяцев назад, скрыть # ^ |
    Rev. 2  
    Проголосовать: нравится 0 Проголосовать: не нравится

    How do you solve Hidden Permutation using binary search?

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

      I am sorry, mixed it with another problem

    • »
      »
      »
      10 месяцев назад, скрыть # ^ |
      Rev. 2  
      Проголосовать: нравится 0 Проголосовать: не нравится

      Build the answer by prefix.

      Consider the following algorithm:

      Goal: Label the prefix $$$[1, i]$$$ with labels $$$[1, i]$$$, while preserving the relative ordering of the elements. The answer will be complete when we successfully label the entire prefix $$$[1, n]$$$.

      Base Case:

      $$$a[1] = 1$$$

      Inductive Step:

      Assume we have already labeled the prefix $$$[1, i - 1]$$$ using labels from $$$[1, i - 1]$$$, such that the relative ordering with respect to the actual permutation is preserved for all indices $$$j, k \lt i$$$.

      Now we want to extend the labeling to include the $$$i$$$th index. One way to do this is via binary search: We need to find the leftmost number $$$k \in [1, i]$$$ such that $$$a[i] \lt k$$$.
      Then we set $$$a[i] = k$$$, and increment all existing labels from the prefix $$$[1, i - 1]$$$ that are greater than or equal to $$$k$$$.
      If no such $$$k$$$ exists, then we set $$$a[i] = i$$$.
      This step takes $$$\log(i)$$$ queries.

      Reference Code: https://cses.fi/paste/b4b9437e5ed90652ca32d9/

    • »
      »
      »
      9 месяцев назад, скрыть # ^ |
       
      Проголосовать: нравится 0 Проголосовать: не нравится
      // 6
      // YES
      // YES
      // NO
      // NO
      // NO
      // YES
      // NO
      // NO
      // NO
      // ? 1 2
      // ? 1 3
      // ? 2 3
      // ? 3 4
      // ? 1 4
      // ? 1 5
      // ? 3 5
      // ? 5 6
      // ? 4 6
      // ! 3 6 5 2 4 1
      void solve()
      {
          ll n;
          cin >> n;
          vi v;
          v.pb(1);
          iloop(i, 2, n + 1)
          {
              ll low = 0, high = v.size() - 1;
              while (low <= high)
              {
                  ll mid = (low + high) / 2;
                  cout << "?" << sp << v[mid] << sp << i << endl;
                  cout.flush();
                  string s;
                  cin >> s;
                  if (s == "YES")
                      low = mid + 1;
                  else
                      high = mid - 1;
              }
              v.insert(v.begin() + low, i);
          }
          vi ans(n, 0);
          ll c = 1;
          for (auto x : v)
              ans[x - 1] = c++;
          cout << "! ";
          print(ans);
      }
      
»
11 месяцев назад, скрыть # |
Rev. 2  
Проголосовать: нравится 0 Проголосовать: не нравится
Spoiler for Mountain Ranges
»
11 месяцев назад, скрыть # |
 
Проголосовать: нравится +8 Проголосовать: не нравится
Spoiler for Hidden Permutation
  • »
    »
    11 месяцев назад, скрыть # ^ |
    Rev. 2  
    Проголосовать: нравится +8 Проголосовать: не нравится

    Why does std::sort not work?

    Edit: With the help of PeruvianCartel's comment and some independent reseach I did on the day itself, it seems as straightforward as STL's implementation of std::sort uses quick sort whereas std::stable_sort uses merge sort.

  • »
    »
    11 месяцев назад, скрыть # ^ |
     
    Проголосовать: нравится +19 Проголосовать: не нравится

    for anyone doesn't know, stable sort, which is basically merge sort, only have to perform a pretty low number of comparisons. It's not optimal, but it's there (the theoretical optimal number of comparison to sort an array of 1000 elements is 8530, and merge sort accomplish that in 8977, while quick sort accomplish that in around 11000).

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

Binary lifting is not needed in "Visible Building Queries", just answer queries offline and binary search on the stack.

https://cses.fi/paste/92acd924a955ed05c4ba93/

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

Mountain Range can be solved without segment tree, here's my submission with comments Submission

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

Any hints for Minimal Grid Path, string hashing is useless here!

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

    I think you already knew that if right cell is 'A' below one is 'B' then you must only go right cuz that only way to get lexicographically minimum walk from there you can do some form of graph traversal like bfs to construct optimum walk try it

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

    Bfs is correct on this one (You remove all path that is not optimal straight away)

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

    I could do it within time limit by simple bfs and maintaining a visited array.

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

    My BinLift+StringHashing gave MLE . Then saw that we can find the optimal path using level order BFS

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

    It's simply a BFS starting at node (0, 0)

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

    BFS Solution

    #include <bits/stdc++.h>
    #include <ext/pb_ds/assoc_container.hpp>
    using namespace std;
    using namespace __gnu_pbds;
    
    template <class T> using ordered_set = tree<T, null_type, less<T>, rb_tree_tag,tree_order_statistics_node_update>;
    // os.insert(element)
    // os.erase(element)
    // os.find_by_order(k) {iterator to the k'th element 0 indexing}
    // os.order_of_key(k) {count of elements smaller than k}
    
    #define fastio ios::sync_with_stdio(false);cin.tie(NULL)
    #define int long long
    #define endl '\n'
    int INF = 1e18;
    int NINF = -1e18;
    int MOD = 1000000000+7;
    
    void solve();
    
    int32_t main()
    {
        fastio;
        int tests = 1;
        // cin >> tests;
        while(tests--)
        {
            solve();
        }
        return 0;
    }
    
    void solve()
    {
        // input
        int n;
        cin >> n;
    
        vector<string> grid(n);
        for(int i=0; i<n; i++)
            cin >> grid[i];
        
        // solution
        string res;
    
        queue<pair<int, int>> q;
        q.push({0, 0});
    
        bool vis[n][n];
        memset(vis, false, sizeof(vis));
        vis[0][0] = true;
    
        while(!q.empty())
        {
            int sz = q.size();
    
            queue<pair<int, int>> qq(q);
            auto [i, j] = qq.front();
            char mnc = grid[i][j];
            while(!qq.empty())
            {
                auto [i, j] = qq.front();
                qq.pop();
                mnc = min(mnc, grid[i][j]);
            }
    
            res.push_back(mnc);
    
            while(sz--)
            {
                auto [i, j] = q.front();
                q.pop();
                char c = grid[i][j];
                if(c == mnc)
                {
                    if(i+1<n && !vis[i+1][j])
                    {
                        vis[i+1][j] = true;
                        q.push({i+1, j});
                    }
                    if(j+1<n && !vis[i][j+1])
                    {
                        vis[i][j+1] = true;
                        q.push({i, j+1});
                    }
                }
            }
        }
        cout << res;
    }
    
  • »
    »
    9 месяцев назад, скрыть # ^ |
     
    Проголосовать: нравится 0 Проголосовать: не нравится
    #include <bits/stdc++.h>
    using namespace std;
    
    #define int long long
    #define ARUN ios::sync_with_stdio(false); cin.tie(nullptr);
    
    int32_t main() {
        ARUN;
        int n;
        cin >> n;
        vector<vector<char>> mat(n, vector<char>(n));
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                cin >> mat[i][j];
    
        queue<pair<int, int>> q;
        vector<vector<int>> vis(n, vector<int>(n, 0));
        q.emplace(0, 0);
        vis[0][0] = 1;
        string ans = string(1, mat[0][0]);
        while (!q.empty()) {
            int s = q.size();
            char mini = 'Z';
            vector<pair<int, int>> tmp;
            while (s--) {
                auto [x, y] = q.front();
                q.pop();
                auto nextState = [&](int nx, int ny) {
                    if (nx >= n || ny >= n || vis[nx][ny]) return;
                    vis[nx][ny] = 1;
                    tmp.emplace_back(nx, ny);
                    mini = min(mini, mat[nx][ny]);
                };
                nextState(x + 1, y);
                nextState(x, y + 1);
            }
            if (tmp.empty()) break;
            for (auto& [x, y] : tmp) {
                if (mat[x][y] == mini) {
                    q.emplace(x, y);
                }
            }
            ans += mini;
        }
    
        cout << ans << '\n';
        return 0;
    }
    
    
»
11 месяцев назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

can someone give me segment tree(doesnt matter if it is lazy or regular) problems that are on codeforces?

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

For the Range Interval Queries, I'm struggling to find out why my implementation of Merge Sort Tree gets TLE

https://cses.fi/paste/f21d39c5ee195b1bc607b8/

In fact, I solved it using Wavelet Tree and after I tried submitting the official solution from CSES that uses Merge Sort Tree and even their code gets TLE as well

Any thoughts?

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

Mountain Range I don't use segment tree. This is my code https://cses.fi/paste/902973a8389f6ae2c6bd6a/

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

Can someone explain me how do we use segment tree for finding mode in a given interval?

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

can anyone pls share their Binary lifting solution of Visible building queries (from range queries), I am facing TLE on one test case,

my code : https://cses.fi/paste/079399dd9980b4b8c9ea9c/

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

Thank you very much!

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

can anyone help me with Sliding Window Advertisement and Xor Pyramid Row.

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

    For xor pyramid row, look at the case where k=2^something. Then, for the general case, just write k as sum of powers of 2.

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

      I'm very bad with bits, can you elaborate more?

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

        Sorry, I meant the case where n-k is a power of 2.

        Solution for n-k=power of 2
        Full solution
»
10 месяцев назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

Can anyone tell me the correct problem statement of Mountain Range I am not able to understand it

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

    There are n mountains in a row, each with a specific height. You can start from any mountain, you can go from mountain a to mountain b if height a is strictly greater than height b and all of mountains in that range([a, a+1, a+2, ..., b] you can go to b if h[a] > max(h[a+1], h[a+2], ... h[b] same thing if a > b). and now the question is what is the maximum number of mountains you can visit on your route?

    consider this array: [20, 15, 17, 35, 25, 40, 12, 19, 13, 12], if you start from 5th mountain you cant go, but if you start from 8 mountain you can go to 7, 9 and 10 mountains.

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

For Distinct Values Subsequences, remember to subtract 1 to account for the empty subsequence

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

Here is my solution for the Mountain Range, it's a simple solution (no segment tree/fenwik tree/any new sorting techniques). just start from the highest mountain and run a dp such that i'th index is updated using the nearest higher mountain at left and right of it(if they exist).

#include <bits/stdc++.h>
using namespace std;
// typedef long long ll;
// #define int ll

void solve(int tc = 0) {
    int n;
    cin >> n;
    std::vector<int> v(n), l(n, -1), r(n, -1), ls(n, -1), rs(n, -1);
    std::vector<pair<int, int>> p(n);
    for (int i = 0; i < n; i++) {
        cin >> v[i];
        p[i] = {v[i], i};
    }
    sort(p.rbegin(), p.rend());
 
    stack<int> st;
    for (int i = 0; i < n; i++) {
        while (!st.empty() && v[st.top()] <= v[i]) {
            st.pop();
        }
        if (!st.empty())  {
            l[i] = st.top();
        }
        st.push(i);
    }
 
    while (!st.empty()) {
        st.pop();
    }
 
    for (int i = n - 1; i >= 0; i--) {
        while (!st.empty() && v[st.top()] <= v[i]) {
            st.pop();
        }
        if (!st.empty())  {
            r[i] = st.top();
        }
        st.push(i);
    }


    vector<int> dp(n, 1);

    for (auto &[x, y] : p) {
        if (r[y] != -1) {
            dp[y] = max(dp[y], dp[r[y]] + 1);
        }
        if (l[y] != -1) {
            dp[y] = max(dp[y], dp[l[y]] + 1); 
        }
    }

    cout << *max_element(dp.begin(), dp.end());
 
 
}
 
int32_t main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
 
    int O_O = 1;
    // cin >> O_O;
    while (O_O--) {
        solve();
        cout << "\n";
    }
 
    return 0;
}
»
10 месяцев назад, скрыть # |
Rev. 2  
Проголосовать: нравится 0 Проголосовать: не нравится

I don't seem to understand the Mountain Range statement quite well.

Could anyone explain to me this test case from cses:

Spoiler
»
10 месяцев назад, скрыть # |
 
Проголосовать: нравится +4 Проголосовать: не нравится
Fixed Length Walk Queries
»
10 месяцев назад, скрыть # |
Rev. 2  
Проголосовать: нравится +3 Проголосовать: не нравится

I have not seen anyone posting construction section, here are my solutions to some of them (couldn't solve last 3)

Inverse Inversions

solution
code

Monotone Subsequences

solution
code

Third Permutation

solution
code

Permutation Prime Sums

solution
code

Chess Tournament

solution
code

I would be really thankful if someone tell me how to solve Distinct Sums Grid

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

Can anyone show me how to solve Hidden Permutation using merge sort?

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

please provide the full solution of Hidden Permutation

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

Hi, I was trying to solve the problem Advanced Graph — Course schedule 2 : https://cses.fi/problemset/task/1757,

My approach is follows :

If we have to do course "a" before course "b", then add a edge from b->a in the graph .

Now in this graph run DFS for each component and keep a track of the minimum node value you have in the subtree.

Now, sort the adj list based on the minimum node value in each subtree, so that If there a multiple ways to do projects "i" then I greedily choose the branch that would lead to do the next-smallest option available as the question requires

Now once, I have sorted the adj_list , I run a dfs and push the node in the result vector once you have traverse all of it's children , this is done in dfs2

I couldn't figure what is wrong in my code, it fails one test-case on CSES, also I believe the run time is O(n+nlogm), so it should pass given the constraints but it gives TLE on one test case

My code link : https://cses.fi/paste/3febffc363f54010ceed11/

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

In the Range Interval Queries problem, I tried merge sort tree with fractional cascading to make the queries in 2 * log(n) but it still gives TLE. Here is my code

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

Range Interval Queries can also be solved using Binary Indexed Tree.

#elements with index in range [l, r]

= #elements in the whole array

- #element in the range [1, l — 1]

- #element in the range [r+1, n]

Each item on the right of the equation can be calculated separately with sorting of the queries.

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

For mountain range for every element you can come on it from either the nearest element on left greater than it or the nearest element on right that is greater than it so we take max of these two this can be implemented in o(n) so a better solution ig

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

Isn't the solution to the Minimal Grid Path problem derivable using BFS? Writing a DP solution for it feels quite unintuitive.

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

Can someone please give a detailed explanation of what SegTree node is going to store in distinct value queries 2 ? if SegTree is going to be of minimum Si for an interval , then how will we manage updates fast ?

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

For "Visible Building Queries". I think I did something totally different than binary lifting https://cses.fi/paste/7ad0015ccbfbb625eaafe3/

I firstly, used segment tree to store the visible buildings list at each node (kind of merge sort tree), by nature of the constraint they will be sorted.

Now to query (L,R), First get the answer from the LEFT subtree, without any constraint, but for the right subtree, there is a constraint that only elements larger than the MAX of left subtree will be visible. So we propagate this threshold to the right subtree after computing the left subtree and then finally we merge both of the answers from left and right.

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

    Code Link

    I have also did a similar type of thing, i haven't created the merge sort trees I have used the pre-calculated answers from the left section to calculate the visible building in right subtree and finally store the height and visible building in the parent node.

    This approach is space optimized.

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

For Minimal Grid Path in DP section. Here each (x,y) goes atmost once in queue and atmost twice in vector f. So its O(n^2). Let me know if I'm wrong in TC. Note: Its Accepted in CSES.

// this is code

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define mod 1000000007

int binpow(int a,int b,int m)
{
    int res=1;
    a=a%m;
    while (b!=0){
        if (b&1){
            res=(res*a)%m;
        }
        a=(a*a)%m;
        b=b>>1;
    }
    return res;
}


void solve()
{
    int n;cin>>n;
    int a[n][n];
    for (int i=0;i<n;i++){
        for (int j=0;j<n;j++){
            char ch;cin>>ch;
            a[i][j]=ch-'A';
        }
    }
    queue <array<int,2>> q;
    q.push({0,0});
    char ch='A'+a[0][0];
    cout<<ch;
    vector vis(n,vector<bool>(n));
    vis[0][0]=true;
    while (!q.empty()){
        int sz=q.size();
        vector <array<int,3>> f;
        while (sz--){
            auto [x,y]=q.front();
            q.pop();
            if (x+1<n && !vis[x+1][y]){
                while (!f.empty() && a[x+1][y]<f.back()[0]) f.pop_back();
                if (f.empty() || f.back()[0]==a[x+1][y]) f.push_back({a[x+1][y],x+1,y});
            }
            if (y+1<n && !vis[x][y+1]){
                while (!f.empty() && a[x][y+1]<f.back()[0]) f.pop_back();
                if (f.empty() || f.back()[0]==a[x][y+1]) f.push_back({a[x][y+1],x,y+1});
            }
        }
        if (f.empty()) break;
        char ch='A'+f.back()[0];
        cout<<ch;
        for (auto [c,x,y]:f){
            if (!vis[x][y]){
                //cout<<x<<" "<<y<<'\n';
                q.push({x,y}),vis[x][y]=true;
            }
        }
    }
    cout<<"\n";
}
int32_t main()
{
    ios_base::sync_with_stdio(0); cin.tie(0);cout.tie(0);
    int t=1;
    //cin>>t;
    while (t--){
        solve();
    }
    return 0;
}



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

Thanks will be useful!!

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

https://cses.fi/paste/86e988fee98ea502edda28/

For the range interval queries, my solution which is O(q(logn)^2) giving TLE while submission.