[ASC47B]Borderless

题目

题意概要
称长度为 n n n 的字符串 ⟨ s 1 , s 2 , s 3 , … , s n ⟩ \langle s_1,s_2,s_3,\dots,s_n\rangle s1,s2,s3,,sn 有一个 B o r d e r \tt{Border} Border ⟨ s 1 , s 2 , s 3 , … , s d ⟩ ( 0 < d < n ) \langle s_1,s_2,s_3,\dots,s_d\rangle(0s1,s2,s3,,sd(0<d<n) ,当且仅当 ∀ k ∈ [ 1 , d ] , s k = s n − d + k \forall k\in[1,d],s_k=s_{n-d+k} k[1,d],sk=snd+k

称一个字符串为 B o r d e r l e s s \tt{Borderless} Borderless ,当且仅当其不存在 B o r d e r \tt{Border} Border

求字典序第 k k k 小的长度为 n n n 0 / 1    B o r d e r l e s s 0/1\;\tt{Borderless} 0/1Borderless 串。

数据范围与约定
k ≤ 1 0 18 , n ≤ 64 k\le 10^{18},n\le 64 k1018,n64 ,并且保证一定存在 k k k 个长度为 n n n 0 / 1    B o r d e r l e s s 0/1\;\tt{Borderless} 0/1Borderless 串。

思路

考虑一位一位的确定(就像在 0 / 1 T r i e 0/1\tt{ Trie} 0/1Trie 上一样)。

f ( x ) f(x) f(x) 表示长度为 x x x B o r d e r l e s s \tt{Borderless} Borderless 字符串的个数,可以考虑减去不合法的。

f ( x ) = 2 x − ∑ i = 1 ⌊ x 2 ⌋ 2 x − 2 i ⋅ f ( i ) f(x)=2^x-\sum_{i=1}^{\lfloor\frac{x}{2}\rfloor}2^{x-2i}\cdot f(i) f(x)=2xi=12x2x2if(i)

枚举的是非法方案中的最短 B o r d e r \tt{Border} Border 长度为 i i i ,直接将其钦定在首尾,用 f ( i ) f(i) f(i) 确保没有更短的;剩下的 x − 2 i x-2i x2i 个位置就随便填。注意到最短 B o r d e r \tt{Border} Border 是不会超过长度的一半的。

现在,由于我们已经确定了更高位,我们就得改变一下转移系数。注意:下面的 f ( x ) f(x) f(x) 都隐含着“前缀为 s ′ s' s ”的条件。

在已经填充了 d d d 位的前缀之后,假设 f ( i ) ( 0 < i ≤ d ) f(i)(0f(i)(0<id) 表示前 i i i 位是否为 B o r d e r l e s s \tt{Borderless} Borderless ,是则为一,否则为零。那么可以这样转移(对于 d < x dd<x 的情况):

  1. d ≤ ⌊ x 2 ⌋ d\le\lfloor\frac{x}{2}\rfloor d2x 时,仍然是枚举最短的 B o r d e r \tt{Border} Border
    f ( x ) = 2 x − d − ∑ i = 1 d − 1 2 x − d − i ⋅ f ( i ) − ∑ i = d ⌊ x 2 ⌋ 2 x − 2 i ⋅ f ( i ) = 2 x − d − ∑ i = 1 ⌊ x 2 ⌋ 2 x − i − max ⁡ ( d , i ) ⋅ f ( i ) \begin{aligned} f(x)&=2^{x-d}-\sum_{i=1}^{d-1}2^{x-d-i}\cdot f(i)-\sum_{i=d}^{\lfloor\frac{x}{2}\rfloor}2^{x-2i}\cdot f(i)\\ &=2^{x-d}-\sum_{i=1}^{\lfloor\frac{x}{2}\rfloor}2^{x-i-\max(d,i)}\cdot f(i) \end{aligned} f(x)=2xdi=1d12xdif(i)i=d2x2x2if(i)=2xdi=12x2ximax(d,i)f(i)
  2. d > ⌊ x 2 ⌋ d>\lfloor\frac{x}{2}\rfloor d>2x 时,比较蛋疼,因为后方受到了一些限制。用 a i a_i ai 表示前缀是否有长度为 i i i B o r d e r \tt{Border} Border ,若有,那么 a i = 0 a_i=0 ai=0 ,否则 a i = 1 a_i=1 ai=1 。可以写出
    f ( x ) = 2 x − d − ∑ i = 1 x − d 2 x − d − i ⋅ f ( i ) − ∑ i = x − d + 1 ⌊ x 2 ⌋ a i + d − x f ( i ) f(x)=2^{x-d}-\sum_{i=1}^{x-d}2^{x-d-i}\cdot f(i)-\sum_{i=x-d+1}^{\lfloor\frac{x}{2}\rfloor}a_{i+d-x}f(i) f(x)=2xdi=1xd2xdif(i)i=xd+12xai+dxf(i)

似乎就搞定了?复杂度是 O ( n 3 ) \mathcal O(n^3) O(n3) 的?

代码

因我无评测环境,故此处贴出这篇博客中的代码,作为参考。

#include
#include
#include
typedef unsigned long long ull;
namespace bdc{
    ull hr[64];
    inline int borderless(ull str,int len){
        ull a=0,b=0;
        for(int i=0;i<len;++i){
            a|=str&(1<<i);
            b=(b<<1)|((str>>(len-i))&1);
            if(a==b) return 0;
        }
        return 1;
    }
    inline void print(ull str,int len){
        for(int i=0;i<len;++i) putchar('a'+((str>>i)&1));
    }
    inline ull calc(int n,ull k){
        ull ans=0;
        for(int i=0;i<n;++i){
            hr[0]=1;
            for(int j=1;j<=i;++j){
                hr[j]=borderless(ans,j);
            }
            for(int j=i+1;j<n;++j){
                hr[j]=0;
                for(int k=0;k+k<=j-1;++k){
                    int v=std::max(i,k); // prefix that has been determined
                    if(k+v+1>j){ // if overlapped
                        int vt=k+v+1-j;
                        ull mask=(1ull<<vt)-1;
                        if((ans&mask) == ((ans>>(j-k))&mask)){
                            hr[j]+=hr[k]; // if can into border
                        }
                    }
                    else{
                        hr[j]+=hr[k]<<(j-k-v-1); // if not overlapped, then [border][len(xjbstr|NULLStr)=j-k-v][border]
                    }
                } /// count bordered strings
                hr[j]=(1ull<<(j-i))-hr[j]; /// to borderless
            }
            if(k>hr[n-1]){
                k-=hr[n-1];
                ans|=1ull<<i;
            }
        }
        return ans;
    }
}
int main(){
    int n; ull b;
    while(~scanf("%d%llu",&n,&b)){
        using namespace bdc;
        if(!n && !b) return 0;
        print(calc(n,b),n);
        putchar('\n');
    }
    return 0;
}

你可能感兴趣的:(字符串,动态规划)