CTSC 2016 香山的树

题意

给定 \(n, k\) 和 Lyndon 串 \(s_1\),求长度小于等于 \(n\) 的 Lyndon 串中,按照字典序排在 \(s_1\) 后面 \(k-1\) 名的串 \(s_k\),或报告无解。\(1\le n\le 50, 1\le k\le 10^{15}\)

Lyndon 串:字典序严格小于所有自己真后缀的串

题解

只需要计数拥有某个给定前缀 \(p\) 的 Lyndon 串个数即可,假设这一部分我们能做到 \(\Theta(f(|p|, n))\),那么总复杂度是 \(\Theta\left(|\Sigma|\cdot\sum\limits_{i=1}^{n}f(i, n)\right)\)

考虑一个 Lyndon 串 \(t\),有“字典序小于所有真后缀”这个限制,则我们关心所有 \(t\) 的 Z-Box,即它与它的某个后缀的 LCP。每个 Z-Box 都不能顶到字符串末尾,并且它的下一个字符必须比对应的前缀的下一个字符更大。然而 Z-Box 的求法一般不是在线的,和我们希望的 DP 做法不太适合。注意到一个特定起点的 Z-Box 实际上也就是特定前缀的 Border,我们考虑利用 KMP 而非 Z-Box 求解。尽管 KMP 可以在线地进行转移,我们仍然面对一个问题:设某个以 \(p\) 为前缀的串 \(pq\) 有长度为 \(x \ge |p|\) 的 Border,则我们需要知道 \(q_{x-|p|+1}\),而记录 \(q\) 的每个位置对于 DP 过程而言显然太多了——实际上这样还不如直接枚举所有串。

注意到,当上述情况出现时,一定意味着在 \(pq\) 的后面部分,\(p\) 又出现了一次。而且如果由于上述情况出现了某个后缀的字典序小于等于 \(pq\),那么这个后缀的开头还是 \(p\)。考虑首先计算出 \(f_{i, k}\) 表示长度为 \(i\)不存在长度 \(\le |p|\) 的不合法子串的串个数,把这种串叫做合法串,求解时在 \(p\) 的 KMP 自动机上 DP 即可。注意 DP 结束后还需要从每个状态往后模拟 \(|p|-1\) 次转移,以正确计算 \(p\) 的出现次数。这部分时间复杂度 \(\Theta(n^2|p|\cdot|\Sigma|)\)

随后我们需要首先处理掉有真周期的串,然后就可以将 \(\frac{f_{i,k}}{k}\) 贡献进答案。注意到虽然在上面的部分中我们没有考虑是否存在一个长度 \(\le |p|\) 的 Border,这么做依然是对的。这是因为任何一个非循环串都有唯一的最小表示位置,这个位置一定不存在 Border。假如一个长度为 \(i\) 的串有真周期,以 \(p\) 为前缀,且合法,现在考虑它的最小正周期串 \(r\) 具有什么特点。假如 \(|r|\le |p|\),那么 \(|r|\)\(|p|\) 的一个 Lyndon 串周期(不必是真周期);假如 \(|r| > |p|\),那么 \(r\) 必须也是以 \(p\) 为前缀的合法串,而且可以断言,所有合法串的任意多次重复一定也是一个合法串。下面考虑一个【以 \(p\) 为前缀且 \(p\) 出现了 \(k\) 次的合法串】的 \(x\) 次重复中,有多少次 \(p\) 的出现。可以发现这个数目就是 \(xk\)。于是做完了。

那么总复杂度 \(\Theta(n^4|\Sigma|^2)\)。看起来不太好过,瓶颈在前面的 KMP 自动机 DP。转移的限制是必须通过大于等于最大的非 \(0\) 转移字符的边转移,所以只有向那条边的末端以 \(1\) 的系数转移,或向 \(0\) 以后面的转移个数为系数转移这两种。预处理一下不难做到 \(\Theta(n^4\cdot |\Sigma|)\)

// Author: kyEEcccccc

#include <bits/stdc++.h>

using namespace std;

using LL = long long;
using ULL = unsigned long long;

#define F(i, l, r) for (int i = (l); i <= (r); ++i)
#define FF(i, r, l) for (int i = (r); i >= (l); --i)
#define MAX(a, b) ((a) = max(a, b))
#define MIN(a, b) ((a) = min(a, b))
#define SZ(a) ((int)((a).size()) - 1)

constexpr int N = 55;
constexpr LL INF = 1000000'000000'000001;

int n;
LL k;
string a1;
string ans;

int bor[N];
int nxt[N][26];
int mx[N];

bool chk(const string &s)
{
	int m = s.size();
	bor[0] = -1;
	memset(nxt[0], 0, sizeof (nxt[0]));
	nxt[0][s[0] - 'a'] = 1;
	memset(nxt[1], 0, sizeof (nxt[1]));
	nxt[1][s[0] - 'a'] = 1;
	F(i, 1, SZ(s))
	{
		bor[i] = bor[i - 1];
		while (bor[i] != -1 && s[i] != s[bor[i] + 1]) bor[i] = bor[bor[i]];
		if (s[i] == s[bor[i] + 1]) ++bor[i];
		nxt[i][s[i] - 'a'] = i + 1;
		memcpy(nxt[i + 1], nxt[bor[i] + 1], sizeof (nxt[i + 1]));
	}
	F(i, 0, m)
	{
		mx[i] = -1;
		F(j, 0, 25) if (nxt[i][j] != 0) mx[i] = j;
		assert(mx[i] != -1);
	}

	F(i, 0, SZ(s)) if (mx[i] != s[i] - 'a') return false;
	if (bor[SZ(s)] != -1) return false;
	return true;
}

LL f[N][N][N]; // 长度为 i,当前位于 j,出现了 k 次 p
LL g[N][N];
int ad[N];
bool is_bor[N];

LL cnt(const string& s)
{
	int m = s.size();
	LL res = 0;
	if (chk(s)) ++res;
	F(i, 0, SZ(s)) if (mx[i] != s[i] - 'a') return 0;

	memset(f, 0, sizeof (f));
	f[m][m][1] = 1;
	F(i, m, n - 1)
	{
		F(j, 0, m) F(k, 1, i - m + 1)
		{
			if (f[i][j][k] == INF) return INF;
			f[i + 1][nxt[j][mx[j]]][k + (nxt[j][mx[j]] == m)] += f[i][j][k];
			MIN(f[i + 1][nxt[j][mx[j]]][k + (nxt[j][mx[j]] == m)], INF);
			f[i + 1][0][k] += f[i][j][k] * (25 - mx[j]);
			MIN(f[i + 1][0][k], INF);
		}
	}
	F(j, 0, m) F(k, 1, n - m + 1)
		if (f[n][j][k] == INF) return INF;

	F(j, 0, m)
	{
		ad[j] = 0;
		int t = j;
		F(i, 0, m - 2)
		{
			if (s[i] - 'a' < mx[t]) { ad[j] = -1; break; }
			t = nxt[t][s[i] - 'a'];
			if (t == m) ++ad[j];
		}
	}
	F(i, m + 1, n) F(k, 1, i) g[i][k] = 0;
	F(i, m + 1, n) F(j, 0, m) F(k, 1, i - m + 1)
		if (ad[j] != -1) g[i][k + ad[j]] += f[i][j][k];
	// F(i, m + 1, n) F(k, 1, i) cerr << i << ' ' << k << ' ' << g[i][k] << '\n';

	F(i, 0, m - 1) is_bor[i] = false;
	int t = m - 1;
	while (t != -1) is_bor[t] = true, t = bor[t];
	F(i, 1, m)
	{
		if (!chk(s.substr(0, i))) continue;
		// cerr << i << '\n';
		if (i == m || is_bor[m - 1 - i])
		{
			F(t, 2, n / i) if (i * t > m) g[i * t][t] -= 1;
		}
	}

	F(i, m + 1, n) F(k, 1, i)
	{
		// cerr << "! " << i << ' ' << k << ' ' << g[i][k] << '\n';
		assert(g[i][k] % k == 0);
		res += g[i][k] / k;
		F(t, 2, n / i) g[i * t][k * t] -= g[i][k];
	}

	return res;
}

void dfs(int cur, bool on)
{
	if (cur == n)
	{
		--k;
		return;
	}
	if ((on && cur == SZ(a1) + 1) || (!on && chk(ans))) --k;
	if (k == 0) return;
	if (cur <= SZ(a1) && on)
	{
		ans.push_back(a1[cur]);
		dfs(cur + 1, true);
		if (k == 0) return;
		ans.pop_back();
	}
	F(c, cur <= SZ(a1) && on ? a1[cur] + 1 : 'a', 'z')
	{
		LL x = cnt(ans + (char)c);
		if (x < k) k -= x;
		else
		{
			ans.push_back(c);
			dfs(cur + 1, false);
			return;
		}
	}
	assert(on);
}

signed main(void)
{
	// freopen(".in", "r", stdin);
	// freopen(".out", "w", stdout);
	ios::sync_with_stdio(0), cin.tie(nullptr);

	cin >> n >> k;
	cin >> a1;
	ans = "";
	dfs(0, true);
	// assert(k >= 0);
	if (k == 0) cout << ans << endl;
	else cout << -1 << endl;

	// F(i, 7, 7)
	// {
	// 	ans = "";
	// 	k = i;
	// 	dfs(0, true);
	// 	if (k == 0) cerr << ans << '\n';
	// 	else cerr << -1 << '\n';
	// }
	
	return 0;
}
posted @ 2024-01-11 11:05  kyEEcccccc  阅读(41)  评论(0编辑  收藏  举报