【Luogu】P1631 sequence merge

【Luogu】P1631 Sequence Merge



topic description

There are two length NNThe Monotonous Non-decreasingSequenceA , BA, Bof NA,B,inA , BA,BA,Take a number in B and add them together to getN 2 N^2N2 sums, find thisN 2 N^2NSmallestNN among 2 sumsN. _

input format

The first line is a positive integer NNN ;
second lineNNN integersA 1 , A 2 , … , AN A_1,A_2,…,A_NA1,A2,,AN.
The third row NNN integersB 1 , B 2 , … , BN B_1,B_2,…,B_NB1,B2,,BN

output format

a row of NNN integers, representing theNNN smallest sums.

Sample Input #1

3
2 6 6
1 4 8

Sample output #1

3 6 7

hint

For 50% 50\%50% of the data,N ≤ 1 0 3 N \le 10^3N103 .
For100 % 100\%100% of the data,1 ≤ N ≤ 1 0 5 1 \le N \le 10^51N105 ,1 ≤ ai , bi ≤ 1 0 9 1 \le a_i,b_i \le 10^91ai,bi109




answer



A = { ai ∣ i = 1 , 2 , … N } , B = { bi ∣ i = 1 , 2 , … N } A=\{a_i | i=1,2,…N \ },B=\{b_i | i=1,2,…N\}A={ aii=1,2,N},B={ bii=1,2,N } , the most straightforward way is to use a specification ofN × NN×NN×An array of N to store allai + bj a_i +b_jai+bj, and then the idea based on selection sorting can be used to select the top NNThe N smallest values. But the time complexity of this solution isO ( N 3 ) O(N^3 )O ( N3 ), it will be difficult to pass all test data. So we need to think of other ways.

If you are sensitive enough, you should immediately think of a data structure - the heap. The heap is a built-in O ( n ) O(n)O ( n ) level, both insertion and deletion areO ( log ⁡ n ) O(log⁡n)O ( l o g n ) level data structure. Obviously, in the face ofN × NN × NN×When there are N data, use them to directly establish the specification asN × NN × NN×The heap of N may still time out (the time complexity at this time isO ( N 2 ) O(N^2)O ( N2 )), so other methods need to be considered. Just imagine, can we use this fast-modifying data structure of the heap to dynamically build a data structure with a length ofNNThe data structure of N , and N × NN × Nthrough a relatively fast way of valueN×N data are quickly scanned and valued to update the data structure. To do this, introduce a poset:

Constructing a poset requires that the input arrays be ordered. Observe the two arrays in the question: [ a 1 , a 2 , … , a N ], [ b 1 , b 2 , … , b N ] [a_1,a_2,…,a_N], [b_1,b_2,…, b_N][a1,a2,,aN][b1,b2,,bN] , their length isNNN , the time complexity (fastest) to sort these two arrays separately isO ( N log ⁡ N ) O(N log⁡N)O ( Nl o g N ) , which is acceptable. When the two input arrays are ordered, allai + bj a_i +b_jai+bjThen the following NN can be formedN个偏序集:
{ a 1 + b 1 , a 1 + b 2 , … , a 1 + b N } { a 2 + b 1 , a 2 + b 2 , … , a 2 + b N } … … { a N + b 1 , a N + b 2 , … , a N + b N } \{ a_1+b_1, a_1+b_2, … ,a_1+b_N \} \\ \{ a_2+b_1, a_2+b_2, … ,a_2+b_N \} \\ …… \\ \{ a_N+b_1, a_N+b_2, … ,a_N+b_N \}{ a1+b1,a1+b2,,a1+bN}{ a2+b1,a2+b2,,a2+bN}……{ aN+b1,aN+b2,,aN+bN}

Obviously, in this NNN posets are ordered (monotonically increasing), so for each individual poset, it always satisfies:
ai + bj ≤ ai + b ( j + 1 ) a_i+b_j≤ a_i+b_{(j+1)}ai+bjai+b(j+1)

At the same time, it can be asserted that a 1 + b 1 a_1+b_1a1+b1for all ai + bj a_i+b_jai+bjMinimum value in combination; a N + b N a_N+b_NaN+bNfor all ai + bj a_i+b_jai+bjThe maximum value in the combination; the minimum value in each partial order set is ai + b 1 ( i = 1 , 2 , … , N ) a_i+b_1 (i=1,2,…,N)ai+b1(i=1,2,,N ) , maximum reductionai + b N ( i = 1 , 2 , … , N ) a_i+b_N (i=1,2,…,N)ai+bN(i=1,2,,N ) ; elements of the same column corresponding to different partial orders always haveai + bj ≤ a ( i + 1 ) + bj a_i+b_j≤ a_{(i+1)}+b_jai+bja(i+1)+bj

Based on this property of a partially ordered set, we can do it in O ( 1 ) O(1)Within the time complexity of O ( 1 ) , take out the current minimum value from the constructed partial order set. The specific value strategy is as follows (assuming that there is a constructed length ofNNN 's small rootheap heapheap ):

  • 1. Add the smallest element in each partial order set to the small root heap heapheap 中,即 h e a p = { a 1 + b 1 , a 2 + b 1 , … , a N + b 1 } heap=\{a_1+b_1,a_2+b_1,…,a_N+b_1\} heap={ a1+b1,a2+b1,,aN+b1}
  • 2. Heap heap from the rootTake out the root element (that is, the minimum value in the current heap) from h e a p , assuming that the taken out element isai + bj a_i+b_jai+bj, record the pop-up number + 1;
  • 3. From the partially ordered set where the extracted element is located, extract the element that is only smaller than it (ie ai + b ( j + 1 ) a_i+b_{(j+1)}ai+b(j+1)), insert it into the small root heap heaph e a p ;
  • 4. If the pop-up number is not NNN , then continue to execute 2; otherwise, end the value collection.

When this algorithm ends, we take out all ai + bj a_i+b_jai+bjFront NN in combinationN small value.

proof of correctness

The order in which elements are added to the partially ordered set enables us to ensure that the currently added element is the smallest in the partially ordered set, and the small root heap heaph e a p can guarantee that the elements taken out each time areNNThe smallest of the smallest elements not taken in the N partial order sets, so the correctness is proved .

Use of STL templates

It should be noted that in actual coding, we don't have to build a heap manually, but can use it directly by calling template functions and data structures in STL. The priority queue priority_queueis just a template container provided by STL. The queue has all the functions of the ordinary queue. The difference is that the priority queue adopts the data structure of "heap", which ensures that the first element in the queue is always Is the largest (or smallest) element in the entire priority queue (default is the large root heap).
priority_queueThe class's template parameter list has three parameters:

  • class T, T is the type of elements stored in the priority queue;
  • class Container = vector<T>, Container is the storage structure used at the bottom of the priority queue. It can be seen that the default is vector;
  • class Compare = less<typename Container::value_type> Compar, is the class that defines how elements in the priority queue are compared. What is the way elements in the priority queue are compared? As mentioned earlier, the underlying container of the priority queue is actually a heap, and the elements in the heap have a size relationship. For example, the big root heap: the value of each node is not greater than its parent node, so the top element of the heap is the largest. When we define some classes ourselves, and store the objects instantiated by the class into the heap (at this time, the elements in the heap are certain objects), then the comparison method of the elements in the heap will naturally change. The comparison class in the compiler can only compare built-in types, so the user needs to provide a size comparison mode (that is, the comparison method of the custom class), and fill it in the position of the Compare parameter.

Note: class CompareWhat follows less<typename Container::value_type>is the default comparison class. The default is to compare by less than (less). This comparison method creates a large root heap (so the priority queue defaults to a large root heap). If you need to create a small heap, you need to change less to greater.

less<typename Container::value_type>is defined as follows:

template <class T> 
struct less: binary_function <T,T,bool> {
    
    
	bool operator() (const T& x, const T& y) const {
    
    return x<y;}
};

greater<typename Container::value_type>is defined as follows:

template <class T> 
struct greater: binary_function <T,T,bool> {
    
    
	bool operator() (const T& x, const T& y) const {
    
    return x>y;}
};

"( )" is overloaded inside these two functions to form a functor for use.


method one


The previous algorithm mentioned one point: when an element ai + bj a_i+b_j is taken out from the priority queueai+bj, another element ai + b ( j + 1 ) a_i+b_{(j+1)} that is second (smaller) to this element needs to be taken from the partially ordered set where the element is locatedai+b(j+1)Fill into the priority queue. Theoretically, this is the way to get the value, but in actual operation, it is impossible for us to really create a N × NN×NN×A two-dimensional array of N (as a partially ordered set), because there is a possibility of bursting memory. So how to accomplish this function?
Looking back at the fetch operation of the partial order set, it is not difficult to find one point: when an elementai + bj a_i+b_jai+bj, the element has two indices (i.e. iii andjjj ), where the first indexiii indicates the index of the next value in the A array, and the second index is executingj + 1 j+1j+1 indicates the next index to get the value in the B array. Therefore, we actually only need to buildthe NNN number pairs (one for each element in the priority queue with a length of N) can complete the demand for obtaining the required value from the partial order set.
For this structure (including: the value of the current number, the corresponding index ida in A, and the index idb in B, a total of three attributes), because it is simple enough, we can build the structure by ourselves. But one thing needs to be noted: since this type of data will be put into the priority queue, it is necessary to specify a way to compare the size of the structure (of course, the comparison here must be value), which requires overloading the comparison operator; Going a step further, we require this priority queue to be a "small root heap", so the operator to be overloaded is ">". The AC code written based on this idea is given below:

#include<bits/stdc++.h>
using namespace std;

// 数据声明 
const int N = 100005;
int n,k,a[N],b[N];

// 自定义数据结构 
struct NODE {
    
    
    int value, ida, idb;
	
	// 重载运算符
    bool operator>(const NODE &obj) const {
    
     return value > obj.value;}
}node;

// 定义优先队列 
priority_queue<NODE,vector<NODE>,greater<NODE> > q;

int main(){
    
    
	
	// 录入数据 
    cin>>n;
    for (int i=0; i<n; i++) cin>>a[i];
    for (int i=0; i<n; i++) cin>>b[i];
    
    // 对数组排序 
    sort(a, a+n);
    sort(b, b+n);
    
	// 初始化优先队列 
    for (int i = 0; i<n; i++)
        q.push({
    
    a[i]+b[0], i, 0});
        
    while(n--){
    
    
    	// 取出当前队列的最小值 
        node=q.top(), q.pop();
        
        // 输出 
        cout<<node.value<<" ";
        
        // 从取出元素所在偏序集中选出其次元素(并更改索引 idb 值) 
        node.value = a[node.ida] + b[++node.idb];
        
        // 入队列 
        q.push(node);
    }
    return 0;
}


Method Two


It is said that STL is a warm man, and this question provides sufficient evidence for this.
STL provides a data structure that expresses the meaning of "number pair" pair, and its related properties and functions are defined as follows:

  • Constructor pair<type1, type2>obj(value1, value2): This function can declare and define a pair object;
  • Matryoshka function make_pair( obj1, obj2): This function can construct two existing objects into a new pair type;
  • firstAttribute: pair_obj.first can take out the first attribute value in the object pair_ obj;
  • secondAttribute: pair_obj.second can take out the second attribute value in the object pair_ obj.

According to the previous analysis, we know that now we need a data structure composed of three int types, and the pairstructure can create a class with two elements. Therefore, in order to add one more element, we can create one more pair (just like a nesting doll) pair, that is, create: pair<int, pair<int, int>> obj. In this way, we can set the meanings expressed by the attributes of each layer as follows:

  • obj.first // represents value
  • obj.second.first; // means ida
  • obj.second.second // display idb

In this way, the complete code to solve this problem can be written according to the previous ideas:

#include<bits/stdc++.h>
using namespace std;

// 数据声明 
const int MAX = 100005;
int a[MAX], b[MAX];

priority_queue<pair<int,pair<int,int> >,vector< pair<int,pair<int,int> > >,greater< pair<int,pair<int,int> > > > q;

int main(){
    
    
	int n, tmpa, tmpb;
	
	// 录入数据 
	cin>>n;
	for(int i=0;i<n;i++) cin>>a[i];
	for(int i=0;i<n;i++) cin>>b[i];
	
	// 对数组排序
	sort(a, a+n);
	sort(b, b+n); 
	
	for(int i=0;i<n;i++) q.push(make_pair(a[i]+b[0], pair<int,int>(i, 0)));
	
	// 查找前 k 小值 
	while(n--){
    
    
		// 输出 
		cout<<q.top().first<<" ";
		
		// 取出 ida 和 idb 索引 
		tmpa = q.top().second.first;
		tmpb = q.top().second.second;
		
		// 第一个元素出栈 
		q.pop();
		
		// 更改取出元素所在偏序集中的次元素入栈 
		q.push(make_pair(a[tmpa]+b[tmpb+1], pair<int,int>(tmpa, tmpb+1)));
	}
	return 0;
}	

END


Guess you like

Origin blog.csdn.net/the_ZED/article/details/130222417