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

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 v1,v2,v3;

map<int,int>m;

int N;

memset(a,0,sizeof(a));

memset(c,0,sizeof©);

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>

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
集(>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>

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-7oPPUBMD-1715586731165)]

[外链图片转存中…(img-u3cMLmr0-1715586731166)]

[外链图片转存中…(img-o4uAsEwd-1715586731166)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值