逆序数的还原详解 树状数组+二分

本文介绍通过给定的逆序数序列来还原原始序列的方法,包括每个数字前面有多少个数比其小或比其大的情况,并提供了具体算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、给出每个数字对应的逆序数(每个数字前面有多少个数比其小),现在要你还原出原序列。

例题:POJ2182 链接:http://poj.org/problem?id=2182

Lost Cows
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 10610 Accepted: 6798

Description

N (2 <= N <= 8,000) cows have unique brands in the range 1..N. In a spectacular display of poor judgment, they visited the neighborhood 'watering hole' and drank a few too many beers before dinner. When it was time to line up for their evening meal, they did not line up in the required ascending numerical order of their brands. 

Regrettably, FJ does not have a way to sort them. Furthermore, he's not very good at observing problems. Instead of writing down each cow's brand, he determined a rather silly statistic: For each cow in line, he knows the number of cows that precede that cow in line that do, in fact, have smaller brands than that cow. 

Given this data, tell FJ the exact ordering of the cows. 

Input

* Line 1: A single integer, N 

* Lines 2..N: These N-1 lines describe the number of cows that precede a given cow in line and have brands smaller than that cow. Of course, no cows precede the first cow in line, so she is not listed. Line 2 of the input describes the number of preceding cows whose brands are smaller than the cow in slot #2; line 3 describes the number of preceding cows whose brands are smaller than the cow in slot #3; and so on. 

Output

* Lines 1..N: Each of the N lines of output tells the brand of a cow in line. Line #1 of the output tells the brand of the first cow in line; line 2 tells the brand of the second cow; and so on.

Sample Input

5
1
2
1
0

Sample Output

2
4
5
3
1

/*
0 1 2 1 0
sum(mid):表示在b[mid+1]~b[n]之间有多少个小于mid
(mid - 1):表示一共有多少个数比mid小
则(mid - 1) - sum(mid):表示在b[1]~b[mid-1]之间有多少个小于mid
第一步:在(mid - 1) - sum(mid)序列里面二分查找第一个等于x的数的下标;
第二步:更新每个sum(mid)序列,把x~n的sum()都加上1.
从后向前遍历的过程大致为:

mid:1 2 3 4 5
sum(mid):0 0 0 0 0
(mid - 1) - sum(mid): 0 1 2 3 4
此时i=5,a[5] = 0,二分查找0,得到第一个0的mid是:1;

mid:1 2 3 4 5
sum(mid):1 1 1 1 1
(mid - 1) - sum(mid):-1 0 1 2 3
此时i=4,a[4] = 1,二分查找1,得到第一个1的mid是:3;

mid:1 2 3 4 5
sum(mid):1 1 2 2 2
(mid - 1) - sum(mid):-1 0 0 1 2
此时i=3,a[3] = 2,二分查找2,得到第一个2的mid是:5;

mid:1 2 3 4 5
sum(mid):1 1 2 2 3
(mid - 1) - sum(mid):-1 0 0 1 1
此时i=2,a[2] = 1,二分查找1,得到第一个1的mid是:4;

mid:1 2 3 4 5
sum(mid):1 1 2 3 4
(mid - 1) - sum(mid):-1 0 0 0 0
此时i=1,a[1] = 0,二分查找0,得到第一个0的mid是:2;

最后i=0,循环结束,得到原来的序列为:2 4 5 3 1
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#include <cstring>
using namespace std;
#define maxn 11111
int n;
int bit[maxn],a[maxn],b[maxn];
int lowbit(int x){
	return x&(-x);
}
void update(int x){
	while(x<= n){
		bit[x] += 1;
		x += lowbit(x);
	}
}
int Query(int x){
	int sum = 0;
	while(x > 0){
		sum += bit[x];
		x -= lowbit(x);
	}
	return sum;
}
int Search(int x)
{
	int L = 1, R = n,mid;
	while(L < R)
	{
		mid  = (R + L)/2;
		int num = Query(mid);
		if(mid-1- num >= x){
			R = mid;
		}
		else{
			L = mid + 1;
		}
	}
	return L;
}
int main()
{
#ifdef CDZSC_June
	freopen("t.txt","r",stdin);
#endif
	while(~scanf("%d",&n))
	{
		memset(bit,0,sizeof(bit));
		memset(b,-1,sizeof(b));
		for(int i = 2; i<=n; i++)
		{
			scanf("%d",&a[i]);
		}
		a[1] = 0;
		for(int i = n; i>=1; i--)
		{
			int x = Search(a[i]);
			update(x);
			b[i] = x;
		}
		for(int i = 1; i<=n; i++)
		{
			printf("%d\n",b[i]);
		}
		puts("");
	}
	return 0;
}



2、给出每个数字对应的逆序数(每个数字前面有多少个数比其大),现在要你还原出原序列。

例题:HDU 5592 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5592

ZYB's Premutation

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 740    Accepted Submission(s): 350


Problem Description
ZYB  has a premutation  P ,but he only remeber the reverse log of each prefix of the premutation,now he ask you to 
restore the premutation.

Pair  (i,j)(i<j)  is considered as a reverse log if  Ai>Aj  is matched.
 

Input
In the first line there is the number of testcases T.

For each teatcase:

In the first line there is one number  N .

In the next line there are  N  numbers  Ai ,describe the number of the reverse logs of each prefix,

The input is correct.

1T5 , 1N50000
 

Output
For each testcase,print the ans.
 

Sample Input
  
1 3 0 1 2
 

Sample Output
  
3 1 2
 

Source
 

基本方法和第一种题型相同,假如一个给出一个长度为n的序列:a1~an (ai代表1~i之间有多少个数比其小),则我们就可以得到另一个长度为n序列:n-a1+1~n-an+1(n-ai+1代表1~i之间有多少个数比ai大)。



#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#include <cstring>
using namespace std;
#define maxn 51111
int n;
int bit[maxn],a[maxn],b[maxn];
int lowbit(int x){
    return x&(-x);
}
void update(int x){
    while(x<= n){
        bit[x] += 1;
        x += lowbit(x);
    }
}
int Query(int x){
    int sum = 0;
    while(x > 0){
        sum += bit[x];
        x -= lowbit(x);
    }
    return sum;
}
int Search(int x)
{
    int L = 1, R = n,mid;
    while(L < R)
    {
        mid  = (R + L)/2;
        int num = Query(mid);
        if(mid-1- num >= x){
            R = mid;
        }
        else{
            L = mid + 1;
        }
    }
    return L;
}
int main()
{
#ifdef CDZSC_June
    freopen("t.txt","r",stdin);
#endif
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        memset(bit,0,sizeof(bit));
        for(int i = 1; i<=n; i++)
        {
            scanf("%d",&a[i]);
        }
        for(int i = n; i>=2; i--)
        {
            a[i] = a[i] - a[i-1];
        }
        for(int i = n; i>=1; i--)
        {
            int x = Search(a[i]);
            update(x);
            b[i] = x;
        }
        printf("%d",n - b[1]+1);
        for(int i = 2; i<=n; i++)
        {
            printf(" %d",n - b[i]+1);
        }
        puts("");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值