TOW - Editorial

Tug of War Editorial.

PROBLEM LINK:

Practice
Contest

Author: Aman Singhal
Tester: Abhishek Jugdar, Reyaan Jagnani
Editorialist: Reyaan Jagnani

DIFFICULTY:

EASY

PREREQUISITES

Arrays, Binary Search, Two Pointers

PROBLEM:

Two teams A and B have to face off in a tug of war match in which each round will be played by one of their players. The one who wins must stay for the next round and the other gets eliminated. In case of a draw both the players will get eliminated.
The team whose player is eliminated will send another person from their team.
Players with higher strength always win and players with equal strength will draw.
A team will win a match when all players get eliminated in the opponent team and at least 1 player is left in their team. If both teams are left with no player it will result in a draw.

We know that team A has N players and will send them as they appear basically if i < j, player i will go before player j and also the strength of their i_th player is A_i. We also know team B has M players with i_{th} player having B_i strength. Now we want to find if team B can win a match or not, and if they can, what is the ordering of their team players that will let them win. In case multiple orderings are possible they will decide the lexicographically smallest ordering.

QUICK EXPLANATION

We will compare the value of array A and array B, where array B has to be in decreasing order in order to get lexicographically smallest ordering.
We will sort the array B in decreasing order and then will do the following comparison until at least one of the arrays becomes empty. let u and v be the current element from A and B respectively.
i) If u is greater than v, then it is not possible for B to win.
ii) If u is smaller than v, then we will change u as the next element of A.
iii) If u is equal to v, then we will change both u and v with the next element of A and B respectively.

In the end if A does not win and array B has some non-zero element left then it can win and the lexicographically smallest queue will be the remaining players in B in increasing order and B up to next element in decreasing.

EXPLANATION:

There are few observations which are needed to get the idea of this problem. Let us build the idea of the problem step by step.

Observation 1
If we do not have to output the lexicographically smallest sequence of B, then what sequence always ensures the answer?

It will be B in decreasing order. It is because a player with the highest strength will always result in win or draw, if it is not possible to draw for him then B cannot win. Once he draws with the player of equal strength, the next strength player will play.

Observation 2
How to come up with the smallest lexicographically sequence?
Now we know that is it possible for team B to win or not and if it is possible, we have a sequence which can result in a win. To find out lexicographically smallest sequence, we have to bring players with lower strength ahead. It is because of the definition of lexicographically smallest sequence. So we will check till what players B can win with A and will keep the rest in increasing order in front of current B.

Conclusion- We have to find the players of B which are needed to win the game. After we find them, we can just print the remaining players in increasing order and the required players in decreasing order.

We can implement the above idea using following approaches:

Two pointer

We can implement this by having two pointers, one for array A and other for array B. We will sort the array B in decreasing order, and will make two pointers (say i pointing on A_1, and j pointing on B_1). Now, we will form 3 cases here,
If A_i == B_j, we will increment both i and j.
If A_i > B_j, we will return false, and print “NO”.
If A_i < B_j, we will increment i.
After running till the end, If j is equal to m, which means no player is left in Team B, we will print “NO”. Else, we will print the remaining elements of Team B in increasing order, followed by the eliminated players in decreasing order.
Time Complexity - ((m*logm)+n)

Binary Search Approach

From the observation we can say that if our B is sorted in decreasing order than our queue will look like this -
B_1, B_2, B_3…. B_i, B_m, B_{\text{m-1}}, B_{\text{m-2}} ... B_{\text{i+1}}.

Here, we can apply binary search on the position of i, then check if Team B is winning or not. If it is winning, we will Increase the value of i, else we will decrease the value of i.
Time Complexity - ((n+m)*(logm))

Constructive Approach

In case Team B wins, to make the arrangement lexicographically smallest, we will firstly print those player’s strength who haven’t been eliminated yet.
As we are fighting in decreasing order, the players left have less strength than all the players which were eliminated. So we will sort their strength in increasing order to get the lexicographically smallest arrangement.
Then we will print the strength of the eliminated players in the order of their elimination. (As explained in Observation 2).
Time Complexity - ((m*logm) + n)

Bonus: Proof why our approach gives the lexicographically smallest ordering.

Proof

Let B_win be the lexicographically largest subset of minimum size from B which will result in B to win and B_left be the remaining values of B in increasing order.

Observations

i) B_win will always be in non-increasing order, It is because if B_{\text{i+1}} > B_i then it can always defeat the player that B_i is defeating or have the same strength as B_i, and can be removed from the subset. Last player in B_win will either win or will be the only unplayed player in B_win.
ii) Last element of B_left will always be smaller than the last element of B_win because then it can also defeat the player and B_win cannot be the lexicographically largest subset.

iii) Answer of our problem will always be B_left + B_win. It is because if B_left is lexicographically smallest among all possible combinations then B_left + B_win will be lexicographically smallest. Proof that B_left is lexicographically smallest is :

Let’s say B_left = L_1,L_2,L_3…L_n and B_win = W_1,W_2,… W_m, then if there exists some other lexicographically smallest sequence B_leftmin = M_1,M_2,M_3…M_k which results in B_left to be minimum then, Say for first L_i > M_i then this is only possible if M_i is not present in L and M_i is present in B. Since all values smaller than L_i will be present in L, it will contradict.

SOLUTIONS:

Setter's Solution (Two Pointer Solution) (Python)
T=int(input())
for  _ in range(T):
N,M=map(int,input().split())
A=list(map(int,input().split()))
B=list(map(int,input().split()))

B.sort(reverse=True)
flag,i,j=0,0,0

while(i<N and j<M):
    if (A[i]>B[j]):
        flag=1
        break
    
    elif (A[i]<B[j]):
        i=i+1
        
    else:
        i=i+1
        j=j+1
        
if(flag==1 or j==M):
    print("NO")

else:
    ans=B[(j+1):][::-1]+B[:(j+1)]
    print("YES")
    print(*ans)
Tester's Solution - Binary Search Solution (C++) (abhi2402)
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    int t;
    cin >> t;
    while (t--) {
        int n, m;
        cin >> n >> m;

        vector<int> a(n), b(m);
        for (int i = 0; i < n; i++) {
            cin >> a[i];
        } 

        for (int i = 0; i < m; i++) {
            cin >> b[i];
        }

        sort(b.begin(), b.end());

        auto check = [&] (int mid) -> bool {
            vector<int> c = b;
            reverse(c.begin() + mid, c.end());

            for (int i = 0, j = 0; i < n && j < m; ) {
                if (a[i] > c[j]) j++;
                else if (a[i] < c[j]) i++;
                else {
                    i++;
                    j++;
                }

                if (j == m) return false;
            }
            return true;
        };

        int lo = -1, hi = m;
        while (hi - lo > 1) {
            int mid = (lo + hi) / 2;
            check(mid) ? lo = mid : hi = mid;
        }

        if (lo == -1) {
            cout << "NO\n";
            continue;
        }
        
        reverse(b.begin() + lo, b.end());
        cout << "YES\n";
        for (int x : b) {
            cout << x << ' ';
        }
        cout << '\n';
    }
}
Editorialist's Solution (C++) (Constructive Solution)
#include <bits/stdc++.h>
using namespace std;
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n,m;
        cin>>n>>m;
        vector<int> a(n), b(m);
        for(int i=0; i<n; i++)
        {
            cin>>a[i];
        }
        for(int i=0; i<m; i++)
        {
            cin>>b[i];
        }
        sort(b.begin(), b.end());
        vector<int> ans;
        bool check = 0;
        bool flag = 1;
        vector<int> final;
        int curr = -1;
        for(int i=n-1; i>=0; i--)
        {
            if(a[i]>=curr)
            {
                curr = a[i];
                final.push_back(curr);
            }
        }
        reverse(final.begin(), final.end());
        for(int i=0; i<final.size() && b.size()>0; i++)
        {
            if(final[i]>b.back())
            {
                flag = 0;
                break;
            }
            if(final[i]==b.back())
            {
                ans.push_back(b.back());
                b.pop_back();
            }
            else
            {
                check = 1;
                ans.push_back(b.back());
                b.pop_back();
                break;
            }
        }
        if(flag==0)
        {
            cout<<"NO"<<endl;
            continue;
        }
        if(check==0)
        {
            if(b.size()==0) cout<<"NO"<<endl;
            else 
            {
                ans.push_back(b.back());
                b.pop_back();
                cout<<"YES"<<endl;
                for(int i=0; i<b.size(); i++)
                {
                    cout<<b[i]<<" ";
                }
                for(int i=0; i<ans.size(); i++)
                {
                    cout<<ans[i]<<" ";
                }
                cout<<endl;
            }
        }
        else
        {
            cout<<"YES"<<endl;
            for(int i=0; i<b.size(); i++)
            {
                cout<<b[i]<<" ";
            }
            for(int i=0; i<ans.size(); i++)
            {
                cout<<ans[i]<<" ";
            }
            cout<<endl;
        }
    }
}
4 Likes

Seems the Time Limit was verry strict. (n+m) log n is not passing. :frowning:
solution Is there any bug in the code.?

what’s wrong with this, exact same as setter’s solution.
https://www.codechef.com/viewsolution/55314703

I’m not able to find out the issue here! Followed the editorial , not the code. Please help me out!

https://www.codechef.com/viewsolution/55326532

Ok not to be rude , but the constraint of the problem is ridiculously tight .

My FIRST_SOLUTION here TLE’s at 1.01 sec and I’ve used a Map and Priority Queue , the complexity is (Linear*Logarithmic).

My SECOND_SOLUTION however I replaced it with an Unordered Map with a track of decreasing elements and a Sorted Vector , the complexity still remains the same but little bit optimized and this passes on 0.88 sec.

My question is that this optimization of mine is not huge , but the difference between TLE and AC is (0 and 1) so next time plz make it like a Codeforces round where different solutions with lazy implementations like mine (LOL) also works so we don’t have to waste time on these little optimizations.

Too be honest this was a great problem and I liked the round too . They were pretty interesting. Looking forward to more such rounds . Kudos to all problem setters and the entire team of IIIT pune.
@reyaan44 @aman1108 and everyone else too. :innocent:

Can anyone help me with my code as I am not being able to find the test cases for which my code is giving wrong answer during submission.

Any kind of help will be very much appreciated .

Mycode