您的位置:首页 > Web前端 > JavaScript

[JSOI2016]无界单词

2016-04-25 19:05 597 查看

题目大意

一个只由a和b组成的长度为n的字符串,如果kmp后next
=0,则称之为无界单词。给定n和m,求长度为n的无界单词个数以及第m小的。

第一问

正难则反!

设f[i]表示长度为i的无界单词数量。

那其实只需要算出有界单词数量,再从总数里减去即可。

枚举j为最小的使字符串前j个与后j个相等。

一个其实并不太显然的性质:

j一定小于等于i/2。

为什么呢?

嘿嘿自己想想吧(是我懒得画图了

那么

f[i]=2i−∑i2j=1f[j]∗2i−2∗j

第二问

我们当然是一位一位放,然后统计数量。

那么,就要解决一个问题——已经确定前len位的长度为n的无界单词数量是多少?

用第一问的思路,f[i]的意义改为确定前len位长度为i的无界单词数量。

j与上一问的意义一致。

那么分四种情况讨论:

1、i<=len

此时前i位已经定了,直接判是不是无界单词即可。

2、len<=j

显然这种会算进f[j]里。

剩余随意填,是2i−2∗j

3、j<len<=i−j

可以随意填的是2i−2∗j−(len−j)

4、len>i−j

懒得上图……

我们作j关于i/2的对称点j’

那么i-j=j’

也就是len>j′

此时要想最后出来的字符串是最小为j的有界单词,那么前len-(i-j)位和前len位的后len-(i-j)位应该相同。

直接暴力判。

详见代码。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const ll maxn=80+10;
ll ans[maxn],next[maxn],f[maxn],two[maxn];
ll i,j,k,l,t,n,m,ca,len,last;
ll pd(ll x){
ll i;
fo(i,1,x)
if (ans[i]!=ans[len-x+i]) return 0;
return 1;
}
ll solve(){
ll i,j,t;
fo(i,1,n){
if (i<=len){
if (!next[i]) f[i]=1;else f[i]=0;
continue;
}
f[i]=two[i-len];
fo(j,1,i/2){
if (len<=j) t=two[i-2*j];
else if (j<len&&len<=i-j) t=two[i-2*j-(len-j)];
else t=pd(len-(i-j));
f[i]-=f[j]*t;
}
}
return f
;
}
void insert(ll x){
ans[++len]=x;
if (len==1) return;
while (last&&ans[last+1]!=ans[len]) last=next[last];
if (ans[last+1]==ans[len]) last++;
next[len]=last;
}
int main(){
freopen("word.in","r",stdin);freopen("word.out","w",stdout);
two[0]=1;
fo(i,1,64) two[i]=two[i-1]*2;
scanf("%lld",&ca);
while (ca--){
scanf("%lld%lld",&n,&m);
len=last=0;
printf("%lld\n",solve());
fo(i,1,n){
k=last;
insert(0);
t=solve();
if (t<m){
m-=t;
last=k;
len--;
insert(1);
}
}
fo(i,1,n) printf("%c",ans[i]+'a');
printf("\n");
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: