CF1509-E---组合数学

Seiji Maki doesn’t only like to observe relationships being unfolded, he also likes to observe sequences of numbers, especially permutations. Today, he has his eyes on almost sorted permutations.

A permutation a1,a2,…,an of 1,2,…,n is said to be almost sorted if the condition ai+1≥ai−1 holds for all i between 1 and n−1 inclusive.

Maki is considering the list of all almost sorted permutations of 1,2,…,n, given in lexicographical order, and he wants to find the k-th permutation in this list. Can you help him to find such permutation?

Permutation p is lexicographically smaller than a permutation q if and only if the following holds:

in the first position where p and q differ, the permutation p has a smaller element than the corresponding element in q.
Input
The first line contains a single integer t (1≤t≤1000) — the number of test cases.

Each test case consists of a single line containing two integers n and k (1≤n≤105, 1≤k≤1018).

It is guaranteed that the sum of n over all test cases does not exceed 105.

Output
For each test case, print a single line containing the k-th almost sorted permutation of length n in lexicographical order, or −1 if it doesn’t exist.

cf题目

题目大意:
对于{n}的全排列,现取其中满足 a(i+1)>ai-1的所有序列,按照字典序升序排列全部序列
如{an=3}={[1,2,3],[1,3,2],[2,1,3],[3,2,1]}
对于每个test
输入n与k代表{an=n}排列的第k个排列
求此排列(若不存在,输出-1)

题解:
对于排列{an=n}所包含的序列个数f[n]
满足f[n]=2*f[n-1]
证明如下:

对于f[n]=v[n] (代表排列向量)
有f[n-1]=v[n-1]
则 v[n]={1,v[n-1]}+{v[n-1],n}+{n,n-1,…2,1}-{1,2,3,…n}
有f[n]=2*f[n-1]+1-1=f[n-1]*2

根据此题数据k的范围,我们得出n最大为64时,就已经满足最大的k了,所以当k>f[n]时,即为溢出情况,输出-1.
当有解时,我们找到排列的区间,即f[i]>=k时,从 1 至 n-i 即为顺序不变的排列,然后对于n-i 至 n的序列,再进行排列,由此递归完成所有排列

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#define me(x,y) memset(x,y,sizeof())
using namespace std;
typedef long long ll;
const ll N = 2e5 + 7;
ll n, t, k;
ll f[N];

int main() {
	cin >> t;
	f[0] = f[1] = 1;
	for (ll i = 2; i <= 61; i++) 
		f[i] = f[i - 1] *2;
	while (t--) {
		cin >> n >> k;
		ll bound = min(n, 61ll), now = 0;
		for (ll i = 1; i <= bound; i++)
			if (f[i] >= k) {
				now = i; break;
			}
		if (!now) {
			cout << -1 << "\n";
			continue;
		}
		for (ll i = 1; i <= n - now; i++)printf("%lld ", i);
		ll base = n - now + 1;
		while (1) {
			ll cont = n - base + 1, nxt = 0;
			for (ll i = 1; i <= cont; i++) {
				if (f[cont - i] < k)
					k -= f[cont - i];
				else {
					nxt = i; break;
				}
			}
			for (ll i = base + nxt - 1; i >= base; i--)printf("%lld ", i);
			base += nxt;
			if (base > n)break;
		}
		cout << "\n";
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值