【GDOI模拟】无界单词

Description

你在坐飞机的时候总是喜欢随便写点文字以打发时间。
对于一个单词S,如果存在一个长度L,满足0

Solution

这是一道很神奇的题目。正常的直接用字符串的算法做不了的话那么就是字符串上DP了。
首先,第一个答案很好算,用f[i]表示长度为i的无界单词有多少个。
发现有界单词比较好求,所以可以用总数减去所有的有界单词就是无界单词的个数。首先我们可以知道一个结论就是用一个长度大于 n2 的前缀然后再copy到后面成后缀所形成的有界单词中一定包含一个前缀小于 n2 的前缀与相应的后缀相等,证明显然。所以在减去的所有有界单词中只用减到长度为 n2 的前缀就好了。

f[i]=2ij=1i2f[j]2i2j

因为把前缀copy到后面去之后,中间还会留下一些空格可以随便填进去。
然后第一问就解决了。
第二问很容易想到在每一位枚举是否是a,如果填了a所造成的无界单词还不够k,那么就填b。
问题是怎么求已知前len个字母,求出f[i](表示长度为i的无界单词)。
像第一问一样枚举一个前缀j然后copy到后面去。
因为有len个单词已经是已知的了,所以要分类讨论。
1、如果i< len,然后直接用kmp判断,这是的f[i]不是1就是0。
2、如果j>=len,那么就不用考虑前面已知的字母了,那么答案就加上 2i2j
3、如果j< len,那么就是有一部分是已知的了,就不能随便乱填了,那么答案就加上 2i2j(lenj)
4、但是还有一种情况就是j到i之间的空格不够len填,就是len>j-i,那么就判断一下把前缀为len的放到后缀那里,减掉多余的部分判断一下是否有界或无界就好了,这里我用的是加权hash判断公共子串。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=70,maxx=10007;
ll i,j,k,l,t,n,m,ans,len,u,v;
ll erci[maxn];
ll s[maxn];
ll next[maxn],f[maxn],h[maxn],g[maxn];
ll panduan(int qian,int zong){
    ll hou=zong-qian+1;
    return h[qian]-h[0]*g[qian]==h[zong]-h[hou-1]*g[zong-hou+1];
}
ll doing(){
    ll i,j,k,l;
    memset(f,0,sizeof(f));
    fo(i,1,n){
        if(i<len){
            if(!next[i])f[i]=1;
            continue;
        }
        f[i]=erci[i-len];
        fo(j,1,i/2){
            if(len<=j)l=erci[i-2*j];
            if(len>j)l=erci[i-2*j-(len-j)];
            if(len>i-j)l=panduan(len-(i-j),len);
            f[i]-=f[j]*l;
        }
    }
    return f[n];
}
void insert(ll x){
    ll i,j;
    s[++len]=x;h[len]=h[len-1]*maxx+x;
    if(len==1)return;
    while(u&&s[len]!=s[u+1])u=next[u];
    if(s[len]==s[u+1])u++;
    next[len]=u;
}
int main(){
    freopen("word.in","r",stdin);
    freopen("word.out","w",stdout);
    scanf("%lld\n",&t);
    erci[0]=g[0]=1;
    fo(i,1,64)erci[i]=erci[i-1]*2,g[i]=g[i-1]*maxx;
    while(t--){
        scanf("%lld%lld",&n,&m);
        len=0;
        printf("%lld\n",doing());
        fo(i,1,n){
            v=u;
            insert(0);
            l=doing();
            if(l<m){
                u=v;
                len--;m-=l;
                insert(1);
            }
        }
        fo(i,1,n){
            if(s[i])printf("b");else printf("a");
        }
        printf("\n");
    }
}

你可能感兴趣的:(KMP,GDOI,无界单词,加权hash,字符串上DP)