1010 Lehmer Code (35 分)(思路+详解+树状数组的学习+逆序对+map+vector) 超级详细 Come baby!!!

一:题目

According to Wikipedia: “In mathematics and in particular in combinatorics, the Lehmer code is a particular way to encode each possible permutation of a sequence of n numbers.” To be more specific, for a given permutation of items {A

}, Lehmer code is a sequence of numbers {L hat L is the total number of items from Ato A which are less than A
. For example, given { 24, 35, 12, 1, 56, 23 }, the second Lehmer code L 2 is 3 since from 35 to 23 there are three items, { 12, 1, 23 }, less than the second item, 35.

Input Specification:
Each input file contains one test case. For each case, the first line gives a positive integer N (≤10
5
). Then N distinct numbers are given in the next line.

Output Specification:
For each test case, output in a line the corresponding Lehmer code. The numbers must be separated by exactly one space, and there must be no extra space at the beginning or the end of the line.

Sample Input:

6
24 35 12 1 56 23

结尾无空行
Sample Output:

3 3 1 0 1 0

二:思路:

这个题用到的是树状数组的相关知识 ,其中在树状数组的应用当中有求逆序对一项,但这个题不能直接用,这个题并没有给出 输入的数字的范围 那就无法直接用 求逆序对的方法来求,
因为会出现段错误(因为输入的数 可能大于 500000)
但是我们可以将输入的数进行转换,将他们先进行排序,将他们的下标作为他们的
代替,这样我们就控制住输入数字的范围了,这个题也已经明确指出了输入数字
不重复,所以我们可以大胆的用map,来记录他们的排序

这里举例来说明我们用排好序的下标来代替数,这样控制住数的范围,且结果一样:
在这里插入图片描述
我们可以看到 第一个数 4,4后面比其小的数 还是有 3 个;

三:上码:

 
/**
	思路:这个题并没有给出 输入的数字的范围 那就无法直接用 求逆序对的方法来求
		  因为会出现段错误(因为输入的数 可能大于 500000) 
		  但是我们可以将输入的数进行转换,将他们先进行排序,将他们的下标作为他们的
		  代替,这样我们就控制住输入数字的范围了,这个题也已经明确指出了输入数字
		  不重复,所以我们可以大胆的用map,来记录他们的排序 

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

int c[100005];

int lowbit(int x){
	return x&(-x);
}

//单点更新
void update(int x,int y,int n){
	
	while(x <= n){
		c[x] =  c[x] + y;
		x = x + lowbit(x);		
	}
	
} 

//求取前缀和
int getSum(int pos){
	int sum = 0;
	
	while(pos > 0){
		sum += c[pos];
		pos = pos - lowbit(pos);  
	}
	
	return sum;
} 

 
int main(){
	
	int a[100005];
	vector<int> v1,v2,v3;
	map<int,int>m; 
	
	int N;
	
	memset(a,0,sizeof(a));
	memset(c,0,sizeof(c));
	
	cin >> N;
	
	for(int i = 0; i < N; i++){
		
		int temp;
		cin >> temp;
		
		a[i] = temp;
		v1.push_back(temp);
	}
	
	sort(v1.begin(),v1.end()); 
	
	for(int i = 0; i < N; i++){
		m[v1[i]] = i + 1;
	}
	
	
	for(int i = N - 1; i >= 0; i--){
		
		int num = m[a[i]];		
		update(num,1,100005);
		
		//这里求的是getSum(temp) 表示的是前面比 temp小的个数其中是包含temp本身的 
		int temp = getSum(num) - 1;
		
		v2.push_back(temp);

	}
	
	 for(int i = N-1; i >= 0; i--){
	 	
	 	if(i == N - 1){
	 		cout << v2[i];
		 }else{
		 	cout << ' ' << v2[i]; 
		 }
	 	
	 }
}

在这里插入图片描述

四:介绍相关的知识

1:树状数组

(1):图示:

在这里插入图片描述

(2):相关的介绍

相关知识介绍:
1.理解图 A[i]:表示的是正常的数组
C[i]:表示的是区间的和

eg: c[1] =A[1]
c[2] = A[1] + A[2]
c[6] = A[5] + A[6]

2.那么如何表示C[i] 中 i表示的个数呢 这时候要用到lowbit(i),
lowbit(i) = i & (-i)

eg:lowbit(6) = 2
lowbit(4) = 4

3.i + lowbit[i]:表示其父亲结点的下标
eg:6 + lowbit(6) = 8

i - lowbit(i):表示其左边管辖区域的下标
eg:6 - lowbit(6) = 4

4.相关的函数
(1):求取lowbit(i)

int lowbit (int i){
return i & (-i);
}   

(2):更新单点的值,就是如果你给区间内的某个值增加一定的数,那么其父节点
也会增加相应的值
eg: A[1]比以前大了,那么C[1]也要比以前的大,他的父节点C[2],
也要跟着变大,那么的话,c[2]的父节点也要跟着变大

void update(int x,int y,int n){//参数:表示在x位置增加了y 数组长度为n

while(x <= n){
c[x] = c[x] + y;
x = x + lowbit(x);//求取父节点 
}	

}

(3):求前缀和
eg:求取前6个数的和
sum[6] = A[1] + A[2] + A[3]+ A[4]+ A[5] + A[6]

因为:C[6] = A[5] + A[6]
C[4] = A[1]+A[2]+A[3]+A[4]
那么也就是sum[6] = C[6] + C[4]

int getSum(int pos){
int sum = 0;

while(pos > 0){
sum += C[pos]

pos = pos - lowbit[pos]
}

return sum;
}

2:例题求取区间和

	题目:求出某区间每一个数的和
	输入格式
	第一行包含两个正整数n,m,分别表示该数列数字的个数和
	操作的总个数
	第二行包含n个用空格分隔的整数,其中第i个数字表示数
	列第i项的初始值
	接下来m行每行包含3个整数,表示一个操作,具体如下
	·1 x k含义:将第x个数加上k
	·2 x y含义:输出区间{x,y}内每个数的和
	输出格式
	输出包含若干行整数,即为所有操作2的结果。
	
	
	输入样例: 5 5
			   1 5 4 2 3
			   1 1 3
			   2 2 5
			   1 3 -1
			   1 4 2
			   2 1 4
			   
		输出:14
			  16
/**
	题目:求出某区间每一个数的和
	输入格式
	第一行包含两个正整数n,m,分别表示该数列数字的个数和
	操作的总个数
	第二行包含n个用空格分隔的整数,其中第i个数字表示数
	列第i项的初始值
	接下来m行每行包含3个整数,表示一个操作,具体如下
	·1 x k含义:将第x个数加上k
	·2 x y含义:输出区间{x,y}内每个数的和
	输出格式
	输出包含若干行整数,即为所有操作2的结果。
	
	
	输入样例: 5 5
			   1 5 4 2 3
			   1 1 3
			   2 2 5
			   1 3 -1
			   1 4 2
			   2 1 4
			   
		输出:14
			  16
			  
	
	相关知识介绍:
		1.理解图 A[i]:表示的是正常的数组
		         C[i]:表示的是区间的和 
		  
		    eg:  c[1] =A[1]
		  	     c[2] = A[1] + A[2]
		  	     c[6] = A[5] + A[6]
		   
		2.那么如何表示C[i] 中 i表示的个数呢 这时候要用到lowbit(i),
			lowbit(i) = i & (-i)
		
		    eg:lowbit(6) = 2
		     	lowbit(4) = 4
			
		3.i + lowbit[i]:表示其父亲结点的下标
		  eg:6 + lowbit(6) = 8
		  
		  i - lowbit(i):表示其左边管辖区域的下标
		  eg:6 - lowbit(6) = 4
		  
		4.相关的函数
			(1):求取lowbit(i)
				
				int lowbit (int i){
					return i & (-i);
				}   
		  	
		  	(2):更新单点的值,就是如果你给区间内的某个值增加一定的数,那么其父节点
			  	也会增加相应的值
				  eg: A[1]比以前大了,那么C[1]也要比以前的大,他的父节点C[2],
				  也要跟着变大,那么的话,c[2]的父节点也要跟着变大
				  
				 void update(int x,int y,int n){//参数:表示在x位置增加了y 数组长度为n
				  
					while(x <= n){
						c[x] = c[x] + y;
						x = x + lowbit(x);//求取父节点 
					}	
							
				}
				   
			(3):求前缀和
				eg:求取前6个数的和
					sum[6] = A[1] + A[2] + A[3]+ A[4]+ A[5]	+ A[6]
					
				因为:C[6] = A[5] + A[6]
					  C[4] = A[1]+A[2]+A[3]+A[4]	
				那么也就是sum[6] = C[6] + C[4]	
				
				int getSum(int pos){
					int sum = 0;
					
					while(pos > 0){
						sum += C[pos]
						
						pos = pos - lowbit[pos]
				    }
				    
				    return sum;
				}
				 	    
	
	
	

*/





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

int c[1000] = {0};

int lowbit(int x){
	return x&(-x);
}

//更新单节点 
void update(int x,int y,int n){
	while(x <= n){
		c[x] = c[x] + y;
		x = x + lowbit(x);
	}
}

//求前缀和
int getSum(int pos){
	int sum = 0;
	
	while(pos > 0){
		sum += c[pos];
		pos = pos - lowbit(pos);	
	}
	
	return sum;
} 
 



int main(){
	
	int N,M;
	int a[1000];
	memset(a,0,sizeof(a));
	
	cin >> N >> M;
	
	for(int i = 1; i <= N; i++){
		
		cin >>a[i];
		//这里就是往C[i]中赋值的操作,因为初始的a[i]中的值均为0,故可以开始更新 
		update(i,a[i],N); 
	}
	
	for(int i = 0; i < M; i++){
		
		int operation,num1,num2;
		
		cin >> operation >> num1 >> num2;
		 
		if(operation == 1){
			update(num1,num2,N);
		}else if(operation == 2){
			 cout << getSum(num2) - getSum(num1-1) << endl;
		}else{
			cout << "您的输入有误!!"; 
		} 
			
	}
	
//测试数据	
//	for(int i = 1; i <= N; i++){
//		cout << c[i] << ' ';
//	} 
	
//	cout << a[99];
		
} 


/**

	·1 x k含义:将第x个数加上k
	·2 x y含义:输出区间{x,y}内每个数的和
	输出格式
	输出包含若干行整数,即为所有操作2的结果。
	
	
	输入样例: 5 5
			   1 5 4 2 3
			   1 1 3           
			   2 2 5
			   1 3 -1
			   1 4 2
			   2 1 4
			   
		输出:14
			  16
*/


//5 5
//1 5 4 2 3
//1 1 3
//2 2 5
//1 3 -1
//1 4 2
//2 1 4

3:求逆序对

何为逆序对:
什么是逆序对
设A为一个有n个数字的有序集(>1),其中所有数字各不相同。如果存在正整数i,j
使得1<=i<j<=n而且A[i]>A[j],则<A[i],A[j]>这个有序对称为A的一个逆序对,也称作逆序数。例如
数组(3 1 4 5 2)的逆序对有(3,1)(32)(42)(5,2),共4个

思路:输入的N个数 3 1 4 5 2
每次输入的值都在 A[i](i = 输入的值) 对应的位置 A[3] = 1,A[1] = 1;
其中A[] ,C[],初始化均为0,每次a[i]变化对应的更新C[i]的值
那么前面比其大的数的个数 = i - (前面比其小的个数)
= i - getSum(a[i])


/**
	何为逆序对:
	什么是逆序对
	设A为一个有n个数字的有序集(>1),其中所有数字各不相同。如果存在正整数i,j
	使得1<=i<j<=n而且A[i]>A[j],则<A[i],A[j]>这个有序对称为A的一个逆序对,也称作逆序数。例如
	数组(3 1 4 5 2)的逆序对有(3,1)(32)(42)(5,2),共4个 

	思路:输入的N个数 3 1 4 5 2
	     每次输入的值都在 A[i](i = 输入的值) 对应的位置  A[3] = 1,A[1] = 1;
		 其中A[] ,C[],初始化均为0,每次a[i]变化对应的更新C[i]的值
		 
		 那么前面比其大的数的个数 = i - (前面比其小的个数)
								  =	i - getSum(a[i]) 
	
*/ 


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

int c[1000];

int lowbit(int x){
	return x&(-x);
}

//单点更新
void update(int x,int y,int n){
	
	while(x <= n){
		c[x] =  c[x] + y;
		x = x + lowbit(x);		
	}
	
} 

//求取前缀和
int getSum(int pos){
	int sum = 0;
	
	while(pos > 0){
		sum += c[pos];
		pos = pos - lowbit(pos);  
	}
	
	return sum;
} 

 
int main(){
	
	int a[1000],b[1000];
	int N;
	
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	memset(c,0,sizeof(c));
	
	cin >> N;
	
	for(int i = 1; i <= N; i++){
		
		int temp;
		cin >> temp;
		
		a[temp] = 1;
		update(temp,a[temp],1000);
		
		//这里求的是getSum(temp) 表示的是前面比 temp 小的个数 
		
		cout << i - getSum(temp) << ' ';	
		
	}
	
}

4:对map和vector容器 不熟练的兄弟们可以看下这个连接

map的基本用法
vector的基本用法

五:学习记录

这个题做了3个晚上,从刚拿到直接做,到后来 开始学习树状数组,求区间和,逆序对,慢慢组成了这道题的题解

第一次做的代码:过去俩点


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

int main(){
	
	int N;
	vector<int> v1,v2;
	
	cin >> N;
	
	for(int i = 0; i < N; i++){
		int temp;
		cin >> temp;	
		v1.push_back(temp);
	}
	
	for(int i = 0; i < N; i++){
		
		int count = 0;
		
		for(int j = i+1; j < N; j++){
			
			if(v1[i] > v1[j]){
				count++;
			}
		}
		
		v2.push_back(count);
		
	}
	
	int flag = 0;
	
	for(int i = 0; i < N; i++){
		
		if(flag == 0){
			cout << v2[i];
		}else{
			cout << ' ' << v2[i];
		}
		
		flag = 1;
	}
		
} 

/**
6
24 35 12 1 56 23

3 3 1 0 1 0


*/

第二次做的代码,过去4个点但是其还是有一个点超时


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

int c[1000005];

int lowbit(int x){
	return x&(-x);
}

//单点更新
void update(int x,int y,int n){
	
	while(x <= n){
		c[x] =  c[x] + y;
		x = x + lowbit(x);		
	}
	
} 

//求取前缀和
int getSum(int pos){
	int sum = 0;
	
	while(pos > 0){
		sum += c[pos];
		pos = pos - lowbit(pos);  
	}
	
	return sum;
} 

 
int main(){
	
	int a[1000005],b[10000];
	vector<int> v1,v2;
	int N;
	
	int aa = 1;
	v1.push_back(aa);
	v2.push_back(aa);
	
	memset(a,0,sizeof(a));
	memset(c,0,sizeof(c));
	
	cin >> N;
	
	for(int i = 1; i <= N; i++){
		
		int temp;
		cin >> temp;
	
		v1.push_back(temp);
	}
	
	for(int i = N; i >= 1; i--){
		
		int num = v1[i];
		
		a[num] = 1;
		update(num,a[num],1000005);
		
		//这里求的是getSum(temp) 表示的是前面比 temp小的个数其中是包含temp本身的 
		int temp = getSum(num) - 1;
		
		v2.push_back(temp);

	}
	
	 for(int i = N; i >= 1; i--){
	 	
	 	if(i == N){
	 		cout << v2[i];
		 }else{
		 	cout << ' ' << v2[i]; 
		 }
	 	
	 }
		

}

在这里插入图片描述

加油 陌生人 ,学习就是就像分治算法一样,将一个大问题分解成小问题,然后逐个解决,最后的结果合并成大问题的解,

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天天向上的菜鸡杰!!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值