康托展開是乙個全排列到乙個自然數的雙射。設有n個數(1,2,3,4,…,n),可以有組成不同(n!種)的排列組合,康托展開表示的就是是當前排列組合在n個不同元素的全排列中的名次。
\(x = a[n] * (n - 1)! + a[n - 1] * (n - 2)! + a[n - 2] * (n - 3)! …… + a[2] * 1! + a[1] * 0!\)
例如3的全排列
估計大家應該不用解釋都應該明白了,這裡的a[i]係數是如何來的了
3 > 2, 3 > 1,得到a[3] = 2,2 > 1,得到a[2] = 1
其中的康托展開的數值代表的是當前項在全排類中的數值下標(注意下標從0開始計數)
/*
康托展開計算o(n * n)
未優化,明天把優化的**補上。
*/#includeusing namespace std;
int fac[10] = ;//階乘
int cantor(int a, int n)
return s;
}int main() , n = 5;
printf("%d\n", cantor(a, n));
return 0;
}
94
也就是康托展開的逆過程,就拿上面的例子來說
排列4 5 3 1 2的康托展開值是94。
本來寫了乙個線段樹的,發現好長啊,就沒抄上來,改成了樹狀陣列。當然了資料量小,離散化自然也就可以不用了。/*
逆康托展開計算o(n * n)
康托展開項未優化
*/#includeusing namespace std;
int fac[10] = ;//階乘
int ans[10];
int cantor(int a, int n)
return s;
}void decantor(int s, int n)
}int main() , n = 5;
printf("%d\n", cantor(a, n));
decantor(cantor(a, n), n);
for(int i = 0; i < n; i++)
printf("%d%c", ans[i], i + 1 == n ? '\n' : ' ');
return 0;
}
一開始想成了,可以用並查集維護逆康托展開,發現錯了,,,想了半天也沒想出能用什麼比較簡潔的資料結構來實現o(n)的逆康托展開。
這個 \(o(n^n)\) 的vector成本確實有點高了。
/*
*/#includeusing namespace std;
const int n = 15;
int fac[n] = , ans[n], tree[n], sum, n;
inline int lowbit(int x)
void add(int pos)
}int query(int pos)
return sum;
}int cantor(int a, int n)
return s;
}void decantor(int s, int n)
}int main()
// int a[10] = ;
// do while(next_permutation(a, a + n));
return 0;
}
對康託展開的一些心得
康托展開 對於全排列中形成的乙個陣列,可以知道他是排列中的第幾種.具體公式為 x an n 1 an 1 n 2 ai i 1 a2 1 a1 0 其中,a為整數陣列,並且0 ai一下是一些 比如表示1,2,3,n的排列如 按從小到大排列一共6個。123 132 213 231 312 321 代表...
樹狀陣列求逆序對
題目描述 給定乙個陣列a,它包含n個整數,分別是a 1 a 2 a n 如果存在下標i和j,使得 i j 且 a i a j 同時成立,則 i,j 就為乙個 逆序對 那麼a陣列總共有多少對不同的 逆序對 輸入格式 1247.in 第一行為n 1 n 100000 接下來是n行,每行乙個長整型範圍內的...
樹狀陣列求逆序對
很久以前就學了樹狀陣列,也知道可以用來求逆序對,然而一直沒弄明白他是怎麼實現的 可能當時沒搞清楚逆序對是什麼吧。逆序對就是如果i j a i a j 這兩個就算一對逆序對,簡單來說,所有逆序對的個數和就是找每乙個數的前面有幾個比他的大的數,他們加起來的和就是逆序對的總數。知道什麼是逆序對後就好辦了,...