OPTSORT - Editorial

I came up with the same idea but my approach was a bit different

like you are. given the array 1 2 5 6 4 3 7 9 8
so I first sorted the array for reference → 1 2 3 4 5 6 7 8 9

now the case is we have to find a subsegment [l…r] whose digits are jumbled form of the same segment of the original array

so we can use a xor operation to know about the jumbled word whenever the xor comes 0 → we found a jumbled sequence

but this way it barely passed a test case
can someone please help me out what’s wrong!

my WA solution

#include <bits/stdc++.h>

#define int long long

using namespace std;

int32_t main()

{

int t;

cin>>t;

while(t--){

    int n;

    cin>>n;

    vector<int> v(n);

    for(auto &x:v) cin>>x;

    vector<int> v2=v;

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

    int ans2=v2[n-1]-v2[0];

    int ans=0;

    int st=-1;

    for(int i=0;i<n;i++)

    {

        if(v[i]!=v2[i])

        {

            if(st==-1)

            st=i;

        }

        else

        {

            if(st!=-1)

            {

                ans=ans+v2[i-1]-v2[st];

                st=-1;

            }

        }

    }

    if(st!=-1)

    {

        ans=ans+v2[n-1]-v2[st];

    }

    cout<<min(ans,ans2)<<endl;

}

return 0;

}

As per the editorial ,overlapping sequences must be avoided. So, can someone explain why am i getting wrong answer.

exactly, in the end i just wrote a lot of code and hoped for proof by AC, i get that intuition is an important skill in CP but it’s pretty disheartening when you lose out on time because you tried to come up with some sort of explaination while it was submitted by everyone else with just intuition :confused:

Just sharing my thoughts and experience with this problem:

This should have been the first problem in \text{Division 2}.

I liked this problem very much. This got me thinking for a while, while I was able to solve the first two in less than 10 mins.

The moment I read the statement I couldn’t come up with any solution, not even brute-force (generating a state-space tree for this kind of problem will consume a lot of time).

After thinking for some time, I thought only one sorting would be enough - sort segment A[L\dots R] for some 1 \le L\le R\le N. Further:

  • Let B = \text{sorted}(A).
  • Let i and j be two integers such that A[1\dots i] = B[1\dots i] and A[j\dots N] = B[j\dots N].
  • The answer will be \text{max}(A[i + 1, \dots, j - 1]) - \text{min}(A[i + 1, \dots, j - 1).

But samples betrayed me (Nice framing of samples). My approach worked on samples but not on private test cases.

I missed something, so started working on some small test cases, when I found this.

6
1 5 3 8 7 9
// Expected Output: 3
// My Output: 5

Then I realised there’s not just one segment, there could be more than 1 non-overlapping segments that should be sorted individually to minimise the cost.

Then framed this approach:

  • For each element A[i], find an integer j that satisfies the following conditions

    • j was not used earlier
    • j should be one of the indices where A[i] can be placed. By “\text{can be placed}”, j is one of the positions where A[i] appears in sorted array.
    • |i - j| should be minimum.
  • We end up with several intervals of the form (i, j), that have to be sorted.

  • We still have to merge overlapping intervals - intervals (x_1, y_1) and (x_2, y_2) can be merged iff (y_1 \ge x_2).

  • Now, we have a set of non-overlapping intervals \{(x_1,\ y_1),\ (x_2,\ y_2), \dots, (x_m,\ y_m)\}.

  • The cost of sorting will be \sum_{i = 1}^{i = m} \text{max}(A[x_i\dots y_i ]) - \text{min}(A[x_i\dots y_i]).

Implementation
vector<pair<int, int>> merge_intervals(vector<pair<int, int>>& intervals) {
	sort(intervals.begin(), intervals.end());
	vector<pair<int, int>> ans;
	int i = 0;
	while(i < intervals.size()) {
		int start = intervals[i].first;
		int end = intervals[i].second;
		while(i < intervals.size() && intervals[i].first <= end) {
			end = max(end, intervals[i].second);
			i++;
		}
		ans.push_back({start, end});
	}
	return ans;
}

void solve() {
	int N = 0;
	cin >> N;
	
	vector<int> A(N);
	for(int i = 0; i < N; i++)
	    cin >> A[i];
	    
	vector<int> B(N);
	for(int i = 0; i < N; i++)
	    B[i] = A[i];
	    
	sort(B.begin(), B.end());
	
	map<int, vector<int>> indices;
	FOR(i, N) {
		indices[B[i]].push_back(i);
	}
	
	map<int, int> index_position;
	vector<pair<int, int>> segs;
	
	for(int i = 0; i < N; i++) {
		if(A[i] != B[i]) {
			pair<int, int> seg = make_pair(i, indices[A[i]][index_position[A[i]]]);
			if(seg.second < seg.first) {
				swap(seg.first, seg.second);
			}
			segs.push_back(seg);
		}
		index_position[A[i]]++;
	}
	
	vector<pair<int, int>> intervals = merge_intervals(segs);
	int ans = 0;
	
	for(auto interval : intervals) {
		
		int start = interval.first;
		int end = interval.second;
		
		int max_val = INT_MIN, min_val = INT_MAX;
		for(int i = start; i <= end; i++) {
			if(A[i] > max_val) max_val = A[i];
			if(A[i] < min_val) min_val = A[i];
		}
		
		ans += max_val - min_val;
	}
	cout << ans << '\n';
}
5 Likes

Couldn’t get the whole intuition behind the approach. Unable to understand, can anyone please explain how it’s solved? Video editorials also aren’t available for this problem.

I m writing example below which help me solving this problem :point_down:
1 3 2 5 4 6
cost of optimal sorting is 2(but if u sort from index 2- index 5 it will give 3, which is not optimal).

read the last constraint it says this: Sum of N over all test cases does not exceed 2⋅10e5

113 ^42^91=0
97^58^91=0

Consider it …
3 3 …other elements
1 1…other elements
If you are xoring , all the elements of sorted and unsorted array, then it will come out to be 0 but , you can see it will be wrong.

There was a simpler way to implement this without using multisets . Divide the array into such subarrays that each subarray contains the same set of elements as in the sorted array

Input :: 1(3 2)(6 4 4)(7 8 6)
Sorted: 1(2 3)(4 4 6)(6 7 8)
Ans = 3-2 + 6-4 + 8-6
Implementation
https://www.codechef.com/viewsolution/55462200

1 Like

Added proof for overlapping intervals. The proof of second is meant to be an exercise. There’s only much details you can incorporate in an editorial, without making it too much.

By writing down certain examples and comparing the costs between alternatives, the second thing should be completely clear.

When you had the thought about breaking into earliest matching prefixes, it was worth writing down costs for examples as per this method before dropping it.

it’s more like the test cases are distributed. like if N is 10^5, then there will be 1 test case, or if N is 1 in all test cases then there will be 10^5 test cases. when you add all N values in all test cases, it comes to 2 * 10^5.
So in general, all the different values of N in all test cases won’t exceed 2 * 10^5.

Thanks @taran_1407

Also if anyone has actually solved the first subtask and would like to kindly share their approach. I struggled coming up with a brute force solution.

For first subtask-

C++
//Author- Akshit Monga
//solving for subtask-1
#include <bits/stdc++.h>
using namespace std;

long long readInt(long long l,long long r,char endd){
	long long x=0;
	int cnt=0;
	int fi=-1;
	bool is_neg=false;
	while(true){
		char g=getchar();
		if(g=='-'){
			assert(fi==-1);
			is_neg=true;
			continue;
		}
		if('0'<=g && g<='9'){
			x*=10;
			x+=g-'0';
			if(cnt==0){
				fi=g-'0';
			}
			cnt++;
			assert(fi!=0 || cnt==1);
			assert(fi!=0 || is_neg==false);
			
			assert(!(cnt>19 || ( cnt==19 && fi>1) ));
		} else if(g==endd){
			if(is_neg){
				x= -x;
			}
			assert(l<=x && x<=r);
			return x;
		} else {
			assert(false);
		}
	}
}
string readString(int l,int r,char endd){
	string ret="";
	int cnt=0;
	while(true){
		char g=getchar();
		assert(g!=-1);
		if(g==endd){
			break;
		}
		cnt++;
		ret+=g;
	}
	assert(l<=cnt && cnt<=r);
	return ret;
}
long long readIntSp(long long l,long long r){
	return readInt(l,r,' ');
}
long long readIntLn(long long l,long long r){
	return readInt(l,r,'\n');
}
string readStringLn(int l,int r){
	return readString(l,r,'\n');
}
string readStringSp(int l,int r){
	return readString(l,r,' ');
}


int main()
{
	int T=readIntLn(1,1e5);
	int sumN = 0;
	while(T--){
		int n=readIntLn(1,1e5);
		sumN += n;
		assert(sumN <= 2e5); 
	  	int arr[n];
	  	for(int i=0;i<n;i++){
	  		if(i<n-1) arr[i]=readIntSp(1,1e9);
	  		else arr[i]=readIntLn(1,1e9);
	  	}
	  	int p[n];
	  	vector<pair<int,int>> d;
	  	for(int i=0;i<n;i++)
	  		d.push_back(make_pair(arr[i],i));
	  	sort(d.begin(),d.end());
	  	int c=1;
	  	for(auto i:d){
	  		p[i.second]=c;
	  		c++;
	  	}
	  	int mini=INT_MAX;
	  	int maxi=INT_MIN;
	  	int ans=0;
	  	for(int i=0;i<n;i++){
	  		mini=min(mini,p[i]);
	  		maxi=max(maxi,p[i]);
	  		set<int> vals;
	  		for(int j=0;j<=i;j++){
	  			vals.insert(j+1);
	  		}
	  		for(int j=0;j<=i;j++){
	  			vals.erase(p[j]);
	  		}
	  		if((int)vals.size()==0){
	  			ans+=d[maxi-1].first-d[mini-1].first;
	  			mini=INT_MAX;
	  			maxi=INT_MIN;
	  		}
	  	}
	  	cout<<ans<<'\n';

	}    
		
	assert(getchar()==-1);
	cerr<<"SUCCESS\n";
}
Python
'''Author- Akshit Monga'''
# Subtask- 1 O(n*n)
from sys import stdin, stdout
input = stdin.readline

t = int(input())
for _ in range(t):
    n=int(input())
    arr=[int(x) for x in input().split()]
    vals=[(arr[i],i) for i in range(n)]
    vals=sorted(vals)
    p=[-1 for i in range(n)]
    c=0
    for i in vals:
        p[i[1]]=c
        c+=1
    inds=[]
    for i in range(n):
        if sorted(p[:i+1])==[j for j in range(i+1)]:
            inds.append(i)
    ans=0
    last=0
    for i in inds:
        ans+=max(arr[last:i+1])-min(arr[last:i+1])
        last=i+1
    print(ans)

Please provide link to your submission. The link you provided is of an accepted solution.

Please provide link to your submission.

Consider-

Your output- 4

Considering this submission of yours

Input

1 
4
7 1 7 13 

Expected Output

6

Your Output

12

Even your other submission fails for the above-given test case.

Input

1
6
9 1 6 1 13 17

Expected Output

8

Your Output

0