{LOJ #6041. 「雅礼集训 2017 Day7」事情的相似度 题解

LOJ \#6041. 「雅礼集训 2017 Day7」事情的相似度 题解

解法一

由 parent 树的性质得到,前缀 si,sj 的最长公共后缀实质上就是 i,j 在 SAM 中的 LCA 在 SAM 中的 len。让我们考虑如何处理 (l,r) 区间内的询问。直接考虑求 (l,r) 的贡献是困难的,不妨转变思路,求每个点对于子树内的贡献。对于一个点 x,我们可以容易地用启发式合并来合并出子树内所有的节点。考虑加入子树 y 的贡献后怎么做:不难发现对于一个 a,我们只需要统计离 a 最近的 p,q(pa,aq)。这样我们在合并的时候顺便用 std::set 可以求出。由启发式合并的复杂度可以知道,这样做的总复杂度是 O(nlog2n)。 然后我们得到了 O(nlogn) 个贡献三元组 (l,r,w)m 个询问 (l,r),实际上是一个二维偏序问题,排序后树状数组维护即可。总的时间复杂度是 O(nlog2n) 的。

代码:

#include <bits/stdc++.h>
#define N 2000005
#define M 2
using namespace std;
int n, m;
char s[N];
int tot = 1, lst = 1;
struct SAM {
	int len, fa;
	int s[M];
} sam[N];
set<int>st[N];
void insert(int c, int x) {
	int p = lst, np = lst = ++tot;
	sam[np].len = sam[p].len + 1;
	for (; !sam[p].s[c]; p = sam[p].fa) sam[p].s[c] = np;
	if (!p) sam[np].fa = 1;
	else {
		int q = sam[p].s[c];
		if (sam[q].len == sam[p].len + 1) sam[np].fa = q;
		else {
			int nq = ++tot;
			sam[nq] = sam[q], sam[nq].len = sam[p].len + 1;
			sam[np].fa = sam[q].fa = nq;
			for (; sam[p].s[c] == q; p = sam[p].fa) sam[p].s[c] = nq; 
		}
	}
	st[lst].insert(x);
}

vector<int>v[N];
struct node {
	int r, vl;
};
vector<node>upd[N], que[N];

void dfs(int x) {
	for (auto y : v[x]) {
		dfs(y);
		if (st[x].size() < st[y].size()) swap(st[x], st[y]);
		st[x].insert(-1e9), st[x].insert(1e9);
		for (auto i : st[y]) {
			auto it = --st[x].upper_bound(i);
			if ((*it) != -1e9) upd[*it].push_back({i, sam[x].len});
			it = st[x].lower_bound(i);
			if ((*it) != 1e9) upd[i].push_back({*it, sam[x].len});
		}
		st[x].erase(-1e9), st[x].erase(1e9);
		st[x].insert(st[y].begin(), st[y].end());
		st[y].clear();
	}
}

int lbt(int x) {
	return x & -x;
}
int tr[N];
void add(int x, int vl) {
	while (x <= n) tr[x] = max(tr[x], vl), x += lbt(x); 
}
int ask(int x) {
	int ans = 0;
	while (x) ans = max(tr[x], ans), x -= lbt(x);
	return ans;
}

int ans[N];

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> m;
	cin >> s;
	for (int i = 0; i < n; i++) insert(s[i] - '0', i + 1);
	for (int i = 2; i <= tot; i++) v[sam[i].fa].push_back(i);
	dfs(1);
	for (int i = 1; i <= m; i++) {
		int l, r;
		cin >> l >> r;
		que[l].push_back({r, i});
	}
	for (int i = n; i >= 1; i--) {
		for (auto p : upd[i]) add(p.r, p.vl);
		for (auto p : que[i]) ans[p.vl] = ask(p.r);
	}
	for (int i = 1; i <= m; i++) cout << ans[i] << "\n";
	return 0;
}

解法二

实际上本题是信息搜集题,我将给出符合题目描述的图片,相信大家能推断出本题真正的答案。

picture1

picture2

picture3

picture4

posted @   长安19路  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示