首页 > 其他分享 >{LOJ #6041. 「雅礼集训 2017 Day7」事情的相似度 题解

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

时间:2025-01-15 11:59:40浏览次数:1  
标签:insert sam fa LOJ 题解 len st int Day7

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

解法一

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

代码:

#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

标签:insert,sam,fa,LOJ,题解,len,st,int,Day7
From: https://www.cnblogs.com/Rock-N-Roll/p/18672755

相关文章

  • Codeforces Round 992 (Div. 2) C题解析
    CodeforcesRound992(Div.2) C题解析题目描述......
  • P4770 [NOI2018] 你的名字 题解
    \(\text{P4770[NOI2018]你的名字题解}\)注意到\(l=1,r=|S|\)有整整68分的高分,让我们先来考虑这样的特殊情况。这样的特殊情形实际上要我们求的是\(t\)有多少个本质不同的子串满足其不是\(s\)的子串。正着做看上去有些困难,于是维护\(s,t\)的本质不同公共子串个数,用......
  • 嵌入式杂谈(问题解决一:使用HAL库时keil中代码的分区)
     如图,代码分区代码区域作用Privateincludes引入所需头文件,提供函数声明、类型定义和宏等Privatetypedef创建自定义数据类型,增强代码可读性与维护性Privatedefine定义常量和宏,方便代码修改与简化Privatemacro实现简单代码替换,简化代码逻辑Privatevariables声明和初始化......
  • 题解:AT_abc136_f [ABC136F] Enclosed Points
    传送门Solution对于一个点\(i\),我们将其与其它点匹配,故有\(2^{n-1}\)的方案数,这是答案的初始。对于每个点\((x_i,y_i)\)再建系,四个象限都可能会有点,我们此时考虑四个象限的点如何匹配,才能使\((x_i,y_i)\)包含其中,稍微手玩一下就可以发现,对于一四象限、二三象限的点匹......
  • 记录在虚拟机中达梦数据库DEM安装过程遇到的问题解决方法
    本篇博客是记录了在寒假课程设计中在虚拟机麒麟银河系统安装达梦数据库DEM遇到的各种刁钻问题的解决方法,希望同样遇到这些问题的小伙伴们能够在查看本篇博客后真正解决问题。废话不多说,直接往下看吧! dem服务器的安装与部署1、上传dem和tomcat压缩包2、./dminitpath=/d......
  • ABC382&ABC383题解
    [ABC382C]KaitenSushi题目描述有\(N\)个人,编号从\(1\)到\(N\),他们正在访问一家传送带寿司餐厅。第\(i\)个人的美食级别是\(A_i\)。现在,将会有\(M\)份寿司放置在传送带上。第\(j\)份寿司的美味度为\(B_j\)。每份寿司将按照顺序经过编号为\(1\),\(2\),\(\dots\),\(N......
  • [CCO2021] Through Another Maze Darkly 题解
    思路很牛,代码很难,故写题解记之。前置知识:线段树。洛谷题目链接:P7830[CCO2021]ThroughAnotherMazeDarkly。原题题面很简洁,故不赘述题意。观察(70%)读完题,这是什么东西。我们直接去手玩样例,然后发现几个很有用的信息:一个点被进入一次后,必然会指针朝上。一个点被进......
  • 英雄联盟游戏黑屏有声音 原因分析及问题解决
    英雄联盟作为一款热门游戏,经常有玩家遇上电脑黑屏,但却可以听到游戏内的声音,这是怎么回事?这通常意味着显卡驱动程序可能遇到了某些问题,也可能与游戏文件损坏、系统设置不当等因素有关。以下是一些常见的解决方法:一、显卡问题更新显卡驱动:显卡驱动不兼容或过时可能导致游戏......
  • 题解:AT_abc353_f [ABC353F] Tile Distance
    [ABC353F]TileDistance题解cnblogs题目传送门:洛谷,AtcoderSolution很恶心人的分类讨论题。很显然走大格子大概率比走小格子快。对终点和起点向上下左右枚举大格子,我们就把问题转化为给两个大格子\((a,b)\)、\((c,d)\),求怎样走最快。对角的大格子可以通过\(2\)步相互到......
  • ABC388DEG 题解
    ABC388题解ABCDE+G,rk371。D观察到几个性质:一个人只会在成年的时候得到石头,在成年之后给出石头。第\(i\)个人成年之后,他要给之后的每个人一个石头(除非用光了)。也就是说,假设成年时它的石头数量为\(B_i\),则最终他的石头数量为\(\max(0,B_i-(n-i))\)。因此我们只需......