我是靠谱客的博主 粗心西牛,最近开发中收集的这篇文章主要介绍H - Little T2 and Derangements Gym - 101864H(第k个错排排列,状压DP),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

题意:
i i i不在第 i i i个位置的排列称为错排排列,
求按字典序第 k k k个错排排列。
2 ≤ n ≤ 100 , k ≤ 1 e 15 2≤n≤100,k≤1e15 2n100,k1e15

思路;
对于这类第 k k k个排列的问题,都可以想到,只需要很少的数,就可以构造出很多排列。尽管本题 n n n范围达到100,但实际上最多考虑20个数字就够了。

我们先按照错排公式 n u m [ i ] = ( n − 1 ) ∗ ( n u m [ i − 1 ] + n u m [ i − 2 ] ) num[i]=(n-1)*(num[i-1]+num[i-2]) num[i]=(n1)(num[i1]+num[i2]),算出实际要考虑多少个数字。那么前面不需要考虑的数字,只需要邻项交换就好了。

定义 f [ n e e d ] [ s t a ] f[need][sta] f[need][sta]代表 n e e d need need个数字的排列,且前面一些数字已经放置在了最前面的方案数, s t a sta sta状态代表放置了哪些数字在最前面。

那么问题就和普通的求第 k k k个排列问题一样,我们从前往后每个位置枚举数。枚举当前位置放置 i i i的方案数 n o w now now,如果 n o w < k now<k now<k,那么说明不能放 i i i,就有 k − = n o w k-=now k=now。如果 n o w ≥ k now≥k nowk,那么说明这个位置放 i i i足够满足这么多方案数,所以这个位置就是 i i i

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#include <queue>
#include <cmath>
#include <map>
using namespace std;
const int maxn = 105;
typedef long long ll;

ll num[maxn];//错排总数
ll f[20][(1 << 20) + 7];
void init() {
    num[0] = 1;num[1] = 0;
    for(int i = 2;i <= 20;i++) {
        num[i] = (i - 1) * (num[i - 1] + num[i - 2]);
    }
}
int get(int sta) {
    int cnt = 0;
    while(sta) {
        sta &= (sta - 1);
        cnt++;
    }
    return cnt;
}
ll DP(int need,int sta) {
//    printf("%d %dn",need,sta);
    if(f[need][sta] != -1) return f[need][sta];
    int pos = get(sta);
    if(pos == need) return 1;
    ll &ans = f[need][sta];
    ans = 0;
    for(int i = 0;i < need;i++) {
        if(i == pos) continue;
        if(sta & (1 << i)) continue;
        ans += DP(need,sta ^ (1 << i));
    }
    return ans;
}
void cal(int n,ll all) {
    int need = 0;
    for(int i = 1;i <= 20;i++) {
        if(num[i] >= all) {
            need = i;break;
        }
    }
    if((n - need) & 1) need++;
    vector<int>vec;
    int sta = 0;
    for(int i = 0;i < need;i++) { //第i个位置
        for(int j = 0;j < need;j++) { //放置第j个数字
            if(sta & (1 << j)) continue;
            if(i == j) continue;
            ll now = DP(need,sta ^ (1 << j));
            if(now < all) {
                all -= now;
            } else {
                vec.push_back(j);
                sta ^= (1 << j);
                break;
            }
        }
    }
    for(int i = 1;i <= n - need;i += 2) {
        printf("%d %d ",i + 1,i);
    }
    for(int i = 0;i < vec.size();i++) {
        printf("%d ",vec[i] + n - need + 1);
    }
    printf("n");
}
int main() {
    init();
    memset(f,-1,sizeof(f));
    int T;scanf("%d",&T);
    while(T--) {
        ll n,k;scanf("%lld%lld",&n,&k);
        cal(n,k);
    }
    return 0;
}

最后

以上就是粗心西牛为你收集整理的H - Little T2 and Derangements Gym - 101864H(第k个错排排列,状压DP)的全部内容,希望文章能够帮你解决H - Little T2 and Derangements Gym - 101864H(第k个错排排列,状压DP)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(30)

评论列表共有 0 条评论

立即
投稿
返回
顶部