来自学长的字符串2
我们抬头仰望同一片星空,在万家灯火熄灭的时候……
本来打算先改考试题在再finish,结果鹤了一下午+半个晚上也没鹤对,鹤的T2还被卡了却依然不知道为什么那个贪心是假的……
闲话:今天考题改不出来,好像blog又没了,不过暂且记录一下今天是怎么寄的:T1以为它让我解同余方程结果忘了ex_gcd的板子,打算现推1 h 后没搞出来,溜了,枚举的做法想都没想,谁知道枚举+记忆化就是正解;T2以为它让我分组状压发现不会分组,于是瞎写,到现在还不会正解,子集和dp什么的听起来好玄学啊;T3我以为要用矩阵快速幂进行定长路经统计,写了半天调不出来样例,十分不happy地又跳了,寄感很强烈;T4以为我的枚举排列至少错不了,结果我直接m的全排列都能错?!虽然但是事实就是错了一分都没有。
香山的树
------以下内容来自:鹤的
又学了一个新词汇:lyndon串,一个字符串是lyndon串当且仅当它的最小循环表示是它本身,且它不是循环串。
求长度不超过 n 且字典序大于等于 p 的所有 lyndon 串中,字典序第 k 小的那个。
每次算出以p(是变化的)开头的 lyndon 串个数x。
如果x >= k说明p一定是答案的前缀,如果p不是答案,就给k减1并在p后面填一个a继续做。
如果x < k,说明x小了一定不是答案的前缀,把k - x,如果p末尾有z的话先删掉,然后让p的最后一位变大1继续做。
对于一个合法串,若p在其中出现了c次,那么它有c种循环表示的开头是p,设 s 为某个合法串开头为p的某个循环表示,每个s只会对应一个合法串,就是它的最小循环表示,可以让每个s都给答案1/c的贡献,最终每个合法串都会被恰好算一次。
s的条件:开头为p;s中任意长度为m的子串都不小于p;s不是循环串。
-------以上内容来自:鹤的
先不管s不是循环串的条件,初始答案要算出来所有s是循环串的情况,那么循环节长度为n就是我们要求的答案在乘1/c贡献之前的值。既然s是循环串,它的起点就不再重要,为了方便记录答案,可以找一个起点确定并且终点也确定的位置作为起点,一个很好的选择是从sm+1开始。
设dpi,j,c为从sm+1开始走i步,和 p 正在尝试匹配第 j 个字符(由于s是一个由我们构造出来的串,所以用它转移/统计答案时表示成功地匹配完了 j ,因为它已经统计上了所有的来路,它自己算一种*1),共匹配了 c 次 p 的方案数。长度为 i 时走了 i-1 步,重复出现k次时下标是k-1,因为次数是在开始时累加的,而初值从0开始转移。因为sm+1是起点,所以要强制在最后一步匹配到了p,为了保证合法,每次失配时强制s那位比p那位大。
用g[i]记录用以上算法算出的长度为 i的答案,f[i]表示实际的答案。
由于最终计算f[i]的式子g[i]和f[i]都乘了一个下标,所以在累计g的时候把长度乘上,不要忘了乘1/c,在计算的时候比较大的g[i]表示i*g[i],比较小的由于已经被减完了所以是i*f[i],最终的答案是 f 的和,把下标去掉。
目前认为在判断s这个开头是不是答案是只需要判断K是不是等于1,还有算g[i]的时候gcd(i,k)其实就是k,打算去尝试一下再回来upd。(如果你知道也可以回复thank you)
upd:把那个判断chk()去掉WA 30,把gcd(i, k)改成k WA 10,原因未知……我鹤了个寂寞,我写博客写了个寂寞。
code 是鹤的
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 53;
const int mod = 998244353;
const ll inf = 4e18;
int n, m, nxt[maxn];
ll f[maxn][maxn][maxn], g[maxn], K;
char s[maxn];
#define gcd __gcd
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
inline bool inc() {for( ; m&&s[m]=='z'; s[m--]=0); return m?s[m]++,1:0;}
inline void add(ll &x, ll y) {x = min(inf, x+y);}
inline ll mul(ll x, ll y) {return 1.0*x*y>2*inf?inf:min(inf, x*y);}
inline bool chk()
{
for(int i=2; i<=m; i++)
{
if(strcmp(s+1, s+i)>0) return 0;
}
return 1;
}
inline bool chk2(int j=1)
{
for(int i=2; i<=m; i++)
{
if(s[i] < s[j]) return 0;
if(s[i] == s[j]) j++;
else j = 1;
}
return 1;
}
ll calc()
{
if(!chk2()) return 0;
for(int i=2,j=0; i<=m; i++)
{
while(j && s[j+1] != s[i]) j = nxt[j];
if(s[j+1] == s[i]) j++;
nxt[i] = j;
}
memset(f, 0, sizeof(f));
f[0][nxt[m]+1][0] = 1;
for(int i=0; i<n; i++) for(int j=1; j<=m; j++) for(int k=0; k<=i; k++)
{
if(f[i][j][k])
{
add(f[i+1][j==m?nxt[m]+1:j+1][k+(j==m)], f[i][j][k]);
add(f[i+1][1][k], mul(f[i][j][k], 'z'-s[j]));
}
}
memset(g, 0, sizeof(g));
for(int i=1; i<=n; i++) for(int k=1; k<=i; k++)
{
add(g[i], mul(f[i-1][m][k-1]/(k/gcd(i,k)), i/gcd(i,k)));
}
for(int i=1; i<=n; i++) for(int j=i+i; j<=n; j+=i) if(g[j] != inf) g[j] -= g[i];
for(int i=m; i<=n; i++) if(g[i] == inf) return inf;
ll x = 0; for(int i=m; i<=n; i++) add(x, g[i]/i);
return x;
}
int main()
{
scanf("%d%lld%s", &n, &K, s+1);
m = strlen(s+1);
while(1)
{
ll x = calc();
if(K > x)
{
K -= x;
if(!inc()) puts("-1"), exit(0);
}
else
{
if(chk() && !--K) puts(s+1), exit(0);
s[++m] = 'a';
}
}
return 0;
}