/* 问题是找出逆序数为m的最小n全排列 * 直接暴过去找出第i轮需要第k小数,k = m-(n-1)*(n-2)/2+1 ,当然如果m比较小就k=1了 * 然后用treap找第k小数就可以了。。 * O(nlogn) * 听说有O(n)的规律。。其实我做这题主要为了找规律。。。结果就这么被我水了过去。。 * 好吧继续找规律 */ #include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> using namespace std; int result[50001], r; #define MX 100000 int size, root; long long n, m; struct Node { int l, r, key, rand_fix; int countl, countr; }node[MX]; //left rotate void l_rot(int &index) { int y = node[index].r; node[index].r = node[y].l; node[index].countr = node[y].countl; node[y].l = index; node[y].countl = node[index].countl + node[index].countr + 1; index = y; } //right rotate void r_rot(int &index) { int y = node[index].l; node[index].l = node[y].r; node[index].countl = node[y].countr; node[y].r = index; node[y].countr = node[index].countl + node[index].countr + 1; index = y; } void insert(int &index, int nkey) { if(index == -1) { index = ++size; node[index].l = node[index].r = -1; node[index].rand_fix = rand(); node[index].key = nkey; node[index].countl = node[index].countr = 0; return ; } if(nkey < node[index].key) { node[index].countl++; insert(node[index].l, nkey); if(node[node[index].l].rand_fix > node[index].rand_fix) r_rot(index); } else { node[index].countr++; insert(node[index].r, nkey); if(node[node[index].r].rand_fix > node[index].rand_fix) l_rot(index); } } bool remove(int &index, int nkey) { if(index == -1) return false; if(nkey < node[index].key) { node[index].countl--; return remove(node[index].l, nkey); } else if(nkey > node[index].key) { node[index].countr--; return remove(node[index].r, nkey); } if(node[index].l == -1 && node[index].r == -1) index = -1; else if(node[index].l == -1) index = node[index].r; else if(node[index].r == -1) index = node[index].l; else { if(node[node[index].l].rand_fix < node[node[index].r].rand_fix) { l_rot(index); node[index].countl--; remove(node[index].l, nkey); } else { r_rot(index); node[index].countr--; remove(node[index].r, nkey); } } return true; } int find_value(int nkey, int index) { if(node[index].countl + 1 == nkey) return node[index].key; else if(node[index].countl >= nkey) return find_value(nkey, node[index].l); else return find_value(nkey-node[index].countl-1,node[index].r); } void init() { size = root = -1; for(int i = 1;i <= n;i++) insert(root,i); } int main() { while(scanf("%lld %lld", &n, &m) && n != -1) { init(); for(r = 1;r <= n;r++) { long long ii; long long sum = (n-r)*(n-r-1) / 2; if(sum >= m) ii = 1; else { ii = m - sum + 1; m -= ii-1; } result[r] = find_value((int)ii, root); remove(root,result[r]); } printf("%d", result[1]); for(int i = 2;i <= n;i++) printf(" %d", result[i]); printf("\n"); } }
//贪心法,如果 搞出的东西一定是先由1递增,再由n递减!当然也可以仅由一部分组成 #include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> using namespace std; int main() { long long n, m; bool f[50001]; int result[100001], r; while(scanf("%lld %lld", &n, &m) && n != -1) { r = 1; memset(f,true,sizeof(f)); for(int i = 1;i <= n;i++) { long long nxt = (n-i)*(n-i-1)/2; if(nxt > m) result[r++] = i; else { result[r++] = (m-nxt)+i; f[result[r-1]] = false; break; } } int rr = r-2; if(rr<1) rr = 1; for(int i = n;i >= rr;i--) { if(f[i]) result[r++] = i; } printf("%d", result[1]); for(int i = 2;i <= n;i++) printf(" %d",result[i]); printf("\n"); } }