[QOJ5434] Binary Substrings 题解

\(\text{[QOJ5434] Binary Substrings 题解}\)

有点意思的题目。

要我们求本质不同子串的个数最大,我们先思考满足这一条件时的情形。容易发现的是我们设 \(k\) 为满足 \(2^k+k-1\le n\) 最大的 \(k\),那么最终答案一定满足长度 \(\le k\) 的所有串均出现,\(>k\) 的所有串都不相同。

考虑所有长度是困难且无意义的,发现如果长度为 \(k\) 满足,那么所有长度 \(<k\) 的都满足;若长度为 \(k+1\) 的都满足,那么长度 \(>k+1\) 的都满足。那么只考虑 \(k\)\(k+1\) 的情形。先只考虑满足长度为 \(k\) ,换句话说考虑 \(2^k+k-1=n\)

由于需要构造方案,考虑有欧拉回路解决问题。建一张 \(2^k\) 个点的有向图,编号为 \([0,2^k)\),连边 \(x\to 2x\bmod 2^k\)\(x\to (2x+1)\bmod 2^k\),这个东西的实际意义就是每次增加串的长度,枚举选 \(0\) 还是选 \(1\)。那在这个图上跑一个哈密顿回路就构造了一个合法方案。

那在 \(n\le 10^6\) 的时候怎么跑一个哈密顿回路?答案是暴力 dfs 即可,总之跑得很快,或许这东西复杂度是线性的。

现在考虑 \(2^k+k-1<n\) 的情形。考虑原图每个点都有 \(2\) 条出边,\(2\) 条入边,跑出来环后还剩下的是 \(1\) 条出边,\(1\) 条入边,换句话说原图还剩下一堆环。那我们考虑把这些个小环拼到大环上去,由于每个点剩余的那条出边实际上是确定的,因此对于大环上每个点尝试拼接上小环,能全部拼上就全部拼上,拼不了就把对应的大环上那个点当作起点,绕一圈大环后走完后面的一部分即可。

代码略显抽象。

#include <bits/stdc++.h>
using namespace std;
const int N = 4e5 + 5;
int n, k, mod;
int a[N], cnt;
bool vis[N], fg;
void dfs(int x) {
	a[++cnt] = x;
	vis[x] = 1;
	if (cnt == mod + 1) return fg = 1, void();
	int y = (x << 1) & mod;
	if (!vis[y]) {
		dfs(y);
		if (fg) return;
	}
	if (!vis[y | 1]) {
		dfs(y | 1);
		if (fg) return;
	}
	--cnt;
	vis[x] = 0;
}
int nxt[N];
bool ins[N], yx[N];

int beh(int x) {
	return x + 1 == cnt + 1 ? 1 : x + 1;
}

int main() {
	cin >> n;
	int k = 0;
	while ((1 << (k + 1)) + k <= n) ++k;
	mod = (1 << k) - 1;
	dfs(0);
	for (int i = 1; i <= cnt; i++) {
		int x = a[i], y = (x << 1) & mod;
		if (y != a[beh(i)]) nxt[x] = y;
		else nxt[x] = y | 1;
	}
	int r = n - mod - k, key = 0;
	for (int i = 1; i <= cnt; i++) {
		int x = a[i], ct = 0;
		if (ins[x]) continue;
		while (!ins[x]) {
			ins[x] = 1;
			++ct;
			x = nxt[x];
		}
		if (ct >= r) {
			key = i;
			break;
		}
		else yx[i] = 1, r -= ct;
	}
	for (int i = k - 1; i >= 0; --i) cout << ((a[beh(key)] >> i) & 1);
	for (int i = key + 2; i <= cnt; i++) cout << (a[i] & 1);
	for (int i = 1; i <= key; i++) {
		if (key != cnt || i != 1) cout << (a[i] & 1);
		if (!yx[i]) continue;
		int x = a[i], y = x;
		if (i == key) continue;
		do {
			y = nxt[y];
			cout << (y & 1);
		} while (x != y);
	}
	int x = a[key];
	while (r--) {
		x = nxt[x];
		cout << (x & 1);
	}
	cout << "\n";
	return 0;
}
posted @ 2025-08-21 21:34  长安19路  阅读(10)  评论(0)    收藏  举报