【LOJ】#2078. 「JSOI2016」无界单词

题解

用所有的方案减去有界的方案

我们规定两个前缀后缀相同时长度最短的,设长度为l,因为长度最短所以他们也是无界单词,可以递推

(f[i] = sum_{j = 1}^{lfloor frac{i}{2} floor} f[j] * 2^{i - 2 * j})

计算第k大的时候同理,只需要先对枚举的前缀求一遍next数组,更新f值,然后再统计的时候特判一下后缀需要占用一部分l长前缀的情况

代码

#include <bits/stdc++.h>
#define enter putchar('
')
#define space putchar(' ')
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define MAXN 505
#define mo 99994711
#define pb push_back
#define eps 1e-8
//#define ivorysi
using namespace std;
typedef long long int64;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef double db;
template<class T>
void read(T &res) {
    res = 0;T f = 1;char c = getchar();
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 - '0' + c;
        c = getchar();
    }
    res *= f;
}
template<class T>
void out(T x) {
    if(x < 0) {x = -x;putchar('-');}
    if(x >= 10) out(x / 10);
    putchar('0' + x % 10);
}
int N;
u64 f[105],K;
char s[105];
int nxt[105];
u64 Calc(int l) {
    memset(nxt,0,sizeof(nxt));
    memset(f,0,sizeof(f));
    for(int i = 2 ; i <= l ; ++i) {
        int p = nxt[i - 1];
        while(p && s[p + 1] != s[i]) p = nxt[p];
        if(s[p + 1] == s[i]) nxt[i] = p + 1;
        else nxt[i] = 0;
    }
    for(int i = 1 ; i <= l ; ++i) {
        if(!nxt[i]) f[i] = 1;
    }
    for(int i = l + 1 ; i <= N ; ++i) {
        f[i] = 1LL << (i - l);
        for(int j = 1 ; j <= i / 2 ; ++j) {
            if(l + j <= i) {f[i] -= f[j] * (1LL << (i - j - max(l,j)));}
            else if(f[j]){
                bool flag = 1;
                for(int k = 1 ; k <= l - (i - j); ++k) {
                    if(s[k] != s[k + (i - j)]) {flag = 0;break;}
                }
                f[i] -= flag;
            }
        }
    }
    return f[N];
}
void Solve() {
    read(N);read(K);
    memset(f,0,sizeof(f));
    f[1] = 2;
    for(int i = 2 ; i <= N ; ++i) {
        f[i] = 1LL << i;
        if(i == 64) f[i] = 0;
        for(int j = 1 ; j <= i / 2 ; ++j) {
            f[i] -= f[j] * (1LL << (i - 2 * j));
        }
    }
    out(f[N]);enter;
    for(int i = 1 ; i <= N ; ++i) {
        s[i] = 'a';
        u64 x = Calc(i);
        if(K > x) {K -= x;s[i] = 'b';}
    }
    for(int i = 1 ; i <= N ; ++i) {
        putchar(s[i]);
    }
    enter;
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    int T;
    read(T);
    while(T--) {Solve();}
    return 0;
}
原文地址:https://www.cnblogs.com/ivorysi/p/9547436.html