ASC47B borderless
题目描述
border集合为{NULL,str}的串str称为borderless串.
border即KMP里的那个.
字符集{'a','b'},给定长度n,求第k(给定)小的borderless串.
题解
按位确定,这时我们需要计算对于一个给定的前缀,以它为前缀的长k的borderless串个数.
borderless串个数并不好求,我们考虑求有border的串个数,再容斥掉.
考虑前缀为t的长度为j的有border串个数为ex,长度为j的borderless串个数为2^(j-min(j,t))-ex.
考虑如何计算ex.
枚举最短非空border长度v,首先这个border需是borderless的,否则违反"最短"性.显然这个border的数量已经计算出来了.如果中间没有限制就可以瞎jb填了,如果有限制只有可能是前缀的限制,那么border串的个数除去 2^某个数.具体实现要分类讨论一下,不太难,细节稍多.
其实并不推荐我这样压位.害死人.
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
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(){
freopen("borderless.in","r",stdin);
freopen("borderless.out","w",stdout);
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;
}
经验与教训
位运算害人.(1<<63)==(1<<31).注意正确写法应为(1ull<<63)