一:题目
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 个;
三:上码:
#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);
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
#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];
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 << "您的输入有误!!";
}
}
}
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])
#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);
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;
}
}
第二次做的代码,过去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);
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];
}
}
}
加油 陌生人 ,学习就是就像分治算法一样,将一个大问题分解成小问题,然后逐个解决,最后的结果合并成大问题的解,
|