【题意】将所有二进制(允许前导0)中,同时满足字典序不小于其逆序串,取反串和逆序取反串的串提出来,按字典序排序,求第m个。 n <= 50, k <= 10^16.
【解题方法】
来自论文 Codeforences 泛做
算 法 讨 论
首先显然满足题意的二进制串的首位必须是0.
考虑一位一位地确定答案串。假设已经确定了答案串的前k位,我们假设第k + 1位是0,
则要设法统计出满足条件的串的个数s。
那么如果s < m,则答案串第k + 1位为1,同时m = m − s;否则答案串第k + 1位为0.
于是问题转化为,统计所有长度为n的,前缀为prefix的二进制串中,满足题目要求的串
的个数。
这是一类与数位有关的统计问题,于是很容易想到数位dp。
状态dp[i][rev][inv]表示,当前已经确定了前i位和末i位, rev表示前i位与末i位的逆序是
否相等,inv表示前i位与末i位的逆序取反后是否相等。
状态转移比较显然,我们枚举第i+1位和第n−i位的取值,如果它满足prefix的限制,且
新的串没有违反题目要求(可以利用rev,inv和取值判断), 那么更新rev和inv的状态,并累
加到对应的新状态上。
时间复杂度O(16 ∗ N 2 )
特 别 注 意
注意如果n为奇数,那么dp到正中间一位的时候,这一位会同时作为前i位和末i位的组成部
分,需要特判。
我用dp[l][r][rev][inv]来表示上面的状态,做法上面已经说得很清楚了,上代码吧。
【AC代码】
//
//Created by just_sort 2016/12/12
//Copyright (c) 2016 just_sort.All Rights Reserved
//
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
using namespace std;
using namespace __gnu_pbds;
typedef long long LL;
typedef pair<int, LL> pp;
#define MP(x,y) make_pair(x,y)
const int maxn = 1020;
const int maxm = 1<<12;
const int inf = 0x3f3f3f3f;
typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>order_set;
//head
int n, a[52];
LL k;
bool vis[55][55][2][2];
LL dp[55][55][2][2];
LL dfs(int l, int r, int rev, int inv)
{
if(l > r) return 1;
if(vis[l][r][rev][inv]) return dp[l][r][rev][inv];
vis[l][r][rev][inv] = 1;
LL &ans = dp[l][r][rev][inv];
ans = 0;
for(int i = 0; i < 2; i++){
if(a[l] == -1 || a[l] == i){
for(int j = 0; j < 2; j++){
if(a[r] == -1 || a[r] == j){
if(l < r || i == j){ //特判n为奇数的时候,走到最中间的一位
if(rev || i <= j){
if(inv || i <= 1 - j){
ans = ans + dfs(l + 1, r - 1, rev || i < j , inv || i < 1 - j);
}
}
}
}
}
}
}
return ans;
}
int main()
{
memset(a, -1, sizeof(a));
cin>>n>>k;
k++;
a[0] = 0;
if(dfs(0, n - 1, 0, 0) < k){
printf("-1\n");
return 0;
}
for(int i = 1; i < n; i++){
a[i] = 0;
memset(vis, 0, sizeof(vis));
LL cur = dfs(0, n - 1, 0, 0);
if(cur < k){
k -= cur;
a[i] = 1;
}
}
for(int i = 0; i < n; i++) cout<<a[i];
}