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結(jié)尾無空行
Sample Output:
二:思路:
這個(gè)題用到的是樹狀數(shù)組的相關(guān)知識 ,其中在樹狀數(shù)組的應(yīng)用當(dāng)中有求逆序?qū)σ豁?xiàng),但這個(gè)題不能直接用,這個(gè)題并沒有給出 輸入的數(shù)字的范圍 那就無法直接用 求逆序?qū)Φ姆椒▉砬?#xff0c;
因?yàn)闀霈F(xiàn)段錯(cuò)誤(因?yàn)檩斎氲臄?shù) 可能大于 500000)
但是我們可以將輸入的數(shù)進(jìn)行轉(zhuǎn)換,將他們先進(jìn)行排序,將他們的下標(biāo)作為他們的
代替,這樣我們就控制住輸入數(shù)字的范圍了,這個(gè)題也已經(jīng)明確指出了輸入數(shù)字
不重復(fù),所以我們可以大膽的用map,來記錄他們的排序
這里舉例來說明我們用排好序的下標(biāo)來代替數(shù),這樣控制住數(shù)的范圍,且結(jié)果一樣:
我們可以看到 第一個(gè)數(shù) 4,4后面比其小的數(shù) 還是有 3 個(gè);
三:上碼:
/**思路:這個(gè)題并沒有給出 輸入的數(shù)字的范圍 那就無法直接用 求逆序?qū)Φ姆椒▉砬笠驗(yàn)闀霈F(xiàn)段錯(cuò)誤(因?yàn)檩斎氲臄?shù) 可能大于 500000) 但是我們可以將輸入的數(shù)進(jìn)行轉(zhuǎn)換,將他們先進(jìn)行排序,將他們的下標(biāo)作為他們的代替,這樣我們就控制住輸入數(shù)字的范圍了,這個(gè)題也已經(jīng)明確指出了輸入數(shù)字不重復(fù),所以我們可以大膽的用map,來記錄他們的排序 */ #include<bits/stdc++.h> using namespace std;int c[100005];int lowbit(int x){return x&(-x); }//單點(diǎn)更新 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小的個(gè)數(shù)其中是包含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]; }} }四:介紹相關(guān)的知識
1:樹狀數(shù)組
(1):圖示:
(2):相關(guān)的介紹
相關(guān)知識介紹:
1.理解圖 A[i]:表示的是正常的數(shù)組
C[i]:表示的是區(qū)間的和
eg: c[1] =A[1]
c[2] = A[1] + A[2]
c[6] = A[5] + A[6]
2.那么如何表示C[i] 中 i表示的個(gè)數(shù)呢 這時(shí)候要用到lowbit(i),
lowbit(i) = i & (-i)
eg:lowbit(6) = 2
lowbit(4) = 4
3.i + lowbit[i]:表示其父親結(jié)點(diǎn)的下標(biāo)
eg:6 + lowbit(6) = 8
i - lowbit(i):表示其左邊管轄區(qū)域的下標(biāo)
eg:6 - lowbit(6) = 4
4.相關(guān)的函數(shù)
(1):求取lowbit(i)
(2):更新單點(diǎn)的值,就是如果你給區(qū)間內(nèi)的某個(gè)值增加一定的數(shù),那么其父節(jié)點(diǎn)
也會增加相應(yīng)的值
eg: A[1]比以前大了,那么C[1]也要比以前的大,他的父節(jié)點(diǎn)C[2],
也要跟著變大,那么的話,c[2]的父節(jié)點(diǎn)也要跟著變大
void update(int x,int y,int n){//參數(shù):表示在x位置增加了y 數(shù)組長度為n
while(x <= n){ c[x] = c[x] + y; x = x + lowbit(x);//求取父節(jié)點(diǎn) } }(3):求前綴和
eg:求取前6個(gè)數(shù)的和
sum[6] = A[1] + A[2] + A[3]+ A[4]+ A[5] + A[6]
因?yàn)?#xff1a;C[6] = A[5] + A[6]
C[4] = A[1]+A[2]+A[3]+A[4]
那么也就是sum[6] = C[6] + C[4]
2:例題求取區(qū)間和
題目:求出某區(qū)間每一個(gè)數(shù)的和輸入格式第一行包含兩個(gè)正整數(shù)n,m,分別表示該數(shù)列數(shù)字的個(gè)數(shù)和操作的總個(gè)數(shù)第二行包含n個(gè)用空格分隔的整數(shù),其中第i個(gè)數(shù)字表示數(shù)列第i項(xiàng)的初始值接下來m行每行包含3個(gè)整數(shù),表示一個(gè)操作,具體如下·1 x k含義:將第x個(gè)數(shù)加上k·2 x y含義:輸出區(qū)間{x,y}內(nèi)每個(gè)數(shù)的和輸出格式輸出包含若干行整數(shù),即為所有操作2的結(jié)果。輸入樣例: 5 51 5 4 2 31 1 32 2 51 3 -11 4 22 1 4輸出:1416 /**題目:求出某區(qū)間每一個(gè)數(shù)的和輸入格式第一行包含兩個(gè)正整數(shù)n,m,分別表示該數(shù)列數(shù)字的個(gè)數(shù)和操作的總個(gè)數(shù)第二行包含n個(gè)用空格分隔的整數(shù),其中第i個(gè)數(shù)字表示數(shù)列第i項(xiàng)的初始值接下來m行每行包含3個(gè)整數(shù),表示一個(gè)操作,具體如下·1 x k含義:將第x個(gè)數(shù)加上k·2 x y含義:輸出區(qū)間{x,y}內(nèi)每個(gè)數(shù)的和輸出格式輸出包含若干行整數(shù),即為所有操作2的結(jié)果。輸入樣例: 5 51 5 4 2 31 1 32 2 51 3 -11 4 22 1 4輸出:1416相關(guān)知識介紹:1.理解圖 A[i]:表示的是正常的數(shù)組C[i]:表示的是區(qū)間的和 eg: c[1] =A[1]c[2] = A[1] + A[2]c[6] = A[5] + A[6]2.那么如何表示C[i] 中 i表示的個(gè)數(shù)呢 這時(shí)候要用到lowbit(i),lowbit(i) = i & (-i)eg:lowbit(6) = 2lowbit(4) = 43.i + lowbit[i]:表示其父親結(jié)點(diǎn)的下標(biāo)eg:6 + lowbit(6) = 8i - lowbit(i):表示其左邊管轄區(qū)域的下標(biāo)eg:6 - lowbit(6) = 44.相關(guān)的函數(shù)(1):求取lowbit(i)int lowbit (int i){return i & (-i);} (2):更新單點(diǎn)的值,就是如果你給區(qū)間內(nèi)的某個(gè)值增加一定的數(shù),那么其父節(jié)點(diǎn)也會增加相應(yīng)的值eg: A[1]比以前大了,那么C[1]也要比以前的大,他的父節(jié)點(diǎn)C[2],也要跟著變大,那么的話,c[2]的父節(jié)點(diǎn)也要跟著變大void update(int x,int y,int n){//參數(shù):表示在x位置增加了y 數(shù)組長度為nwhile(x <= n){c[x] = c[x] + y;x = x + lowbit(x);//求取父節(jié)點(diǎn) } }(3):求前綴和eg:求取前6個(gè)數(shù)的和sum[6] = A[1] + A[2] + A[3]+ A[4]+ A[5] + A[6]因?yàn)?#xff1a;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); }//更新單節(jié)點(diǎn) 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]中賦值的操作,因?yàn)槌跏嫉腶[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 << "您的輸入有誤!!"; } }//測試數(shù)據(jù) // for(int i = 1; i <= N; i++){ // cout << c[i] << ' '; // } // cout << a[99];} /**·1 x k含義:將第x個(gè)數(shù)加上k·2 x y含義:輸出區(qū)間{x,y}內(nèi)每個(gè)數(shù)的和輸出格式輸出包含若干行整數(shù),即為所有操作2的結(jié)果。輸入樣例: 5 51 5 4 2 31 1 3 2 2 51 3 -11 4 22 1 4輸出:1416 *///5 5 //1 5 4 2 3 //1 1 3 //2 2 5 //1 3 -1 //1 4 2 //2 1 43:求逆序?qū)?/h2>
何為逆序?qū)?#xff1a;
什么是逆序?qū)?br /> 設(shè)A為一個(gè)有n個(gè)數(shù)字的有序集(>1),其中所有數(shù)字各不相同。如果存在正整數(shù)i,j
使得1<=i<j<=n而且A[i]>A[j],則<A[i],A[j]>這個(gè)有序?qū)ΨQ為A的一個(gè)逆序?qū)?也稱作逆序數(shù)。例如
數(shù)組(3 1 4 5 2)的逆序?qū)τ?3,1)(32)(42)(5,2),共4個(gè)
思路:輸入的N個(gè)數(shù) 3 1 4 5 2
每次輸入的值都在 A[i](i = 輸入的值) 對應(yīng)的位置 A[3] = 1,A[1] = 1;
其中A[] ,C[],初始化均為0,每次a[i]變化對應(yīng)的更新C[i]的值
那么前面比其大的數(shù)的個(gè)數(shù) = i - (前面比其小的個(gè)數(shù))
= i - getSum(a[i])
4:對map和vector容器 不熟練的兄弟們可以看下這個(gè)連接
map的基本用法
vector的基本用法
五:學(xué)習(xí)記錄
這個(gè)題做了3個(gè)晚上,從剛拿到直接做,到后來 開始學(xué)習(xí)樹狀數(shù)組,求區(qū)間和,逆序?qū)?#xff0c;慢慢組成了這道題的題解
第一次做的代碼:過去倆點(diǎn)
#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 233 3 1 0 1 0*/第二次做的代碼,過去4個(gè)點(diǎn)但是其還是有一個(gè)點(diǎn)超時(shí)
#include<bits/stdc++.h> using namespace std;int c[1000005];int lowbit(int x){return x&(-x); }//單點(diǎn)更新 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小的個(gè)數(shù)其中是包含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]; }}}加油 陌生人 ,學(xué)習(xí)就是就像分治算法一樣,將一個(gè)大問題分解成小問題,然后逐個(gè)解決,最后的結(jié)果合并成大問題的解,
總結(jié)
以上是生活随笔為你收集整理的1010 Lehmer Code (35 分)(思路+详解+树状数组的学习+逆序对+map+vector) 超级详细 Come baby!!!的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jsp是什么文件
- 下一篇: 查询在具有最小内存容量的所有PC中具有最