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)

posted @ 2016-04-09 23:46  zball  阅读(156)  评论(0编辑  收藏  举报