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):图示:
(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;
}
题目:求出某区间每一个数的和
输入格式
第一行包含两个正整数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
何为逆序对:
什么是逆序对
设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>
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。