Yibentong 1311: [Example 2.5] Finding reverse order pairs, two solutions (merging, line tree)

Reference Luogu P1908

 

I will explain two methods of tree array and merge sort, line segment tree solution is similar to tree array


① Merge sort solution

First you need to know what is merge sort. Then, we can think of it like this:

If we want to arrange a sequence from small to large, then the left and right subranges are sorted from small to large after each division and merge. We only need to count each number in the right interval and the left interval Just generate as many reverse-order pairs.

If you do n’t understand, look at chestnuts: This is the key point of ans + = mid-i + 1 ;. Understand, no need to memorize


//在某个时候,左区间:  5 6 7  下标为i
//           右区间:  1 2 9  下标为j
//          
//这个时候我们进行合并:
//step 1:由于 5>1,所以产生了逆序对,这里,我们发现,左区间所有还没有被合并的数都比 1 大
//       所以1与左区间所有元素共产生了 3 个逆序对(即tot_numleft-i+1对),统计答案并合并 1 
//step 2:由于 5>2,由上产生了3对逆序对,统计答案并合并 2
//step 3:由于 5<9, 没有逆序对产生,右区间下标 j++
//step 4:由于 6<9, 没有逆序对产生,右区间下标 j++
//step 5:由于 7<9, 没有逆序对产生,右区间下标 j++
//step 6:由于右区间已经结束,正常执行合并左区间剩余,结束

//PS: tot_numleft=3,即左区间总元素个数

Understand it (inexplicably confident)

Since there are no pits in merge sorting, it can be executed normally and counted. Note that the  answer will burst intint! We need to use longlong longlong storage, if you use  printfprintf  output, then don't forget to use % lldlld  !

The complexity is the same as the ordinary merge sort, which is O (Nlog_2N) O (Nlog2 N)

Efficiency: 1682ms / 4.43MB1682ms / 4.43MB  (after data update)

Code:

#include<cstdio>
#include<iostream>
using namespace std;
int n,a[500010],c[500010];
long long ans;

void msort(int b,int e)//归并排序
{
    if(b==e)  
        return;
    int mid=(b+e)/2,i=b,j=mid+1,k=b;
    msort(b,mid),msort(mid+1,e);
    while(i<=mid&&j<=e)
        if(a[i]<=a[j])
            c[k++]=a[i++];
        else
            c[k++]=a[j++],ans+=mid-i+1;//统计答案
    while(i<=mid)
        c[k++]=a[i++];
    while(j<=e)
        c[k++]=a[j++];
    for(int l=b;l<=e;l++)
        a[l]=c[l];
} 

int main()
{
    scanf("%d",&n); 
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    msort(1,n);
    printf("%lld",ans);
    return 0;
}

② Tree array solution

The solution of the tree array is not merged quickly in this problem, the reason will be said later

Q1: Q1: We need to know how to count how many inverse pairs will the ii number and 11th ~ i-1i−1 number constitute?

Ans1: Ans1: Consider constructing a tree-like array based on values. The initial tree-like array is all 00s. Now the number corresponding to the data value is increased by one from left to right according to the sequence, which means that another number appears. Therefore, when looping to item ii, the first i-1i−1 items have been added to the tree-like array. Those in the tree-like array that are larger than a_iai will form a reverse order pair with a_iai because they must appear earlier , So the number of pairs generated in reverse order is i-query (a_i) i−query (ai)

Note: query (a_i): query (ai) means to query the prefix and the 11 ~ a_iai item in the tree array

Q2: Q2: There is not enough space to build a tree array based on a_iai?

Ans2: Ans2: It's really not enough. But all we need is the relative size between the data, we only need to satisfy greater or less than itself, regardless of how large, specifically, let's take a chestnut:

//  1 2 10000
//  1 2 3
//上面两个序列在本题是等效的,因为无论第三项是3还是10000,它都大于第一项和第二项

This inspired us to discretize the data, first sort the data, and then use 11 ~ nn to correspond to the number of nn to express their relative size. It is enough to build a tree array space for the new sequence (n≤5 × 10 ^ 5) ( n≤5 × 105)

Q3: Q3: Will the equal elements cause solution errors? Each number (whether equal or not) corresponds to a new number different?

Ans3: Ans3: If you do n’t deal with it, you will get an error. The key is whether there is an element equal to a_iai that is added before a_iai and its relative size mark is larger. When this happens, two equal numbers will be mistakenly judged as reverse pairs. How to solve it, as long as all the elements that are equal to a_iai, the mark that appears first is also smaller (we only count the relatively larger ones). Specifically, you only need to sort a_iai as the first keyword and the subscript (the first few occurrences) as the second keyword from small to large.

If you are not using the tree array of this method, then your WAWA is probably that you forgot to deal with the problem mentioned in Q3Q3. If RERE, most of the space is open.

At the same time, once again, pay attention to the  answer will burst intint! We need to use longlong longlong storage, if you use  printfprintf  output, then don't forget to use % lldlld  !

Since we not only need to sort, but also build a tree-like array statistics, so although the two are of the same complexity, the tree-like array will be slower here.

Both methods are necessary to master, when the data range is small, the tree array will be faster, both have advantages.

The complexity bottleneck is sorting, O (Nlog_2N) O (Nlog2 N)

Efficiency: 2359ms / 8.38MB: 2359ms / 8.38MB (after data update)

Code:Code:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int tree[500010],rank[500010],n;
long long ans; 
struct point
{
    int num,val;
}a[500010];
inline bool cmp(point q,point w)
{
    if(q.val==w.val)
        return q.num<w.num;
    return q.val<w.val;
}
inline void insert(int p,int d)
{
    for(;p<=n;p+=p&-p)
        tree[p]+=d; 
}
inline int query(int p)
{
    int sum=0;
    for(;p;p-=p&-p)
        sum+=tree[p];
    return sum;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i].val),a[i].num=i;
    sort(a+1,a+1+n,cmp);
    for(int i=1;i<=n;i++)
        rank[a[i].num]=i;
    for(int i=1;i<=n;i++)
    {
        insert(rank[i],1);
        ans+=i-query(rank[i]);
    }
    printf("%lld",ans);
    return 0;
} 
Posted 33 original articles · liked 0 · visits 167

Guess you like

Origin blog.csdn.net/weixin_42790071/article/details/105439821