2025.2.21 日校内模拟赛总结(AC自动机, 期望,倍增)

news2025/2/24 9:44:55

文章目录

  • 时间安排
  • 题解

时间安排

将近两个半小时才过 T 1 T1 T1,后面花一个半小时过了 T 2 T2 T2,最后半个小时写不出 T 3 T3 T3 暴力没有获得分数。

反思:

  1. T 1 T1 T1 这种题要敢于去猜结论打表,由于没有直接猜结论做了很长时间。但是猜到结论后就比较好做。
  2. T 2 T2 T2 时节奏还是不错的,先写了暴力,然后发现暴力复杂度比较玄学。后面又想到了正解。

题解

点我

题意:
给你 n n n 个长度为 m m m 的字符串 T 1 , T 2 , . . . , T n T_1, T_2, ..., T_n T1,T2,...,Tn,保证仅由前 k k k 个小写字母构成。
定义函数 f ( S , T , p ) f(S, T, p) f(S,T,p),其中 S S S 为一个字符串, T T T 为上述的 n n n 个字符号串, p p p 为一个长度为 k k k 的概率数组。每次操作会在 S S S 后面以 p i p_i pi 的概率接上第 i i i 种字符,如果某一时刻 存在某个 i i i 满足 T i T_i Ti S S S 的子串则停止操作。 f ( S , T , p ) f(S, T, p) f(S,T,p) 等于 停止操作时 S S S 的期望长度。
开始时给你一个字符串 R R R,和一个长度为 k k k 的数组 p p p。你需要按顺序执行 Q Q Q 次操作,操作分两种:
1 l c:修改操作,将 R R R 的后 l l l 个字符替换成 c c c
2 l:查询操作,查询 R R R 长度为 l l l 的前缀的函数值 f ( R [ 1 , . . . , l ] , T , p ) f(R[1,...,l], T, p) f(R[1,...,l],T,p)

数据规模:
1 ≤ n ≤ 100 1 \leq n \leq 100 1n100 n × m ≤ 1 0 4 n \times m \leq 10^4 n×m104 1 ≤ k ≤ 26 1 \leq k \leq 26 1k26 1 ≤ ∣ R ∣ ≤ 1 0 4 1 \leq |R| \leq 10^4 1R104 1 ≤ Q ≤ 1 0 5 1 \leq Q \leq 10^5 1Q105

分析:
发现 T T T p p p 是不会变的,多模式串匹配显然考虑 A C AC AC 自动机。那么可以把原问题分解成两个问题:
1.在 A C AC AC 自动机上某个节点以一定概率随机游走,求走到叶子的期望步数。
2. 支持修改字符串后缀,快速求出一个前缀字符串在 A C AC AC 自动机上的对应节点。
我们一个一个看。

对于第一个问题:
首先由于所有 T i T_i Ti 长度相等,因此对应的终止节点就是所有的叶子。将每个节点的期望值看作一个变量,对于节点 p p p,可以列出以下方程:
p p p 为非叶子: E p = 1 + ∑ c p c × E t r p , c E_p = 1 + \sum\limits_{c}p_c \times E_{tr_{p, c}} Ep=1+cpc×Etrp,c
p p p 为叶子: E p = 0 E_p = 0 Ep=0

暴力高消的复杂度为 O ( ( n × m ) 3 ) O((n \times m)^3) O((n×m)3),显然无法通过。
正解感觉很神奇:我们可以用 n n n 个变量表示所有节点的期望值,然后再利用 n n n 个叶子的期望为 0 0 0 得到 n n n 个方程从而在 O ( n 3 + n 2 m ) O(n^3 + n^2m) O(n3+n2m) 的复杂度得到所有节点的期望。
具体做法是这样:由于 t r i e trie trie 树上最多有 n n n 个叶子,因此我们一定能自上到下将树划分成若干条不交链。设 i i i 号点在链上的后继为 n x t i nxt_i nxti,对应的边为 c c c。考虑按照深度 b f s bfs bfs 以此确定每个点的期望如何被变量表示。假设当前队头节点为 u u u,深度为 t t t,那么可以认为前 t t t 层节点的期望都已经被表示过了。枚举 u u u 的出边 t r u , i tr_{u, i} tru,i,分两种情况讨论:

  1. t r u , i ≠ n x t u tr_{u, i} \ne nxt_{u} tru,i=nxtu。若 t r u , i tr_{u, i} tru,i 的深度小于等于 u u u,那么它的期望值一定被表示过了。若 t r u , i tr_{u, i} tru,i 的深度大于 u u u,那么他是一条链的链头,新开一个变量表示它的期望即可。
  2. t r u , i = n x t u tr_{u, i} = nxt_{u} tru,i=nxtu。 那么可以得到 E n x t u = E u − ∑ v ≠ c p v × E t r u , v − 1 p c E_{nxt_{u}} = \frac{E_{u} - \sum\limits_{v\ne c}p_v \times E_{tr_{u, v}} - 1}{p_{c}} Enxtu=pcEuv=cpv×Etru,v1

不难发现每次只会在一条链的链头新开一个变量,因此只会开 n n n 个变量。然后利用 n n n 个叶子的期望为 0 0 0 就可以把所有期望解出来。

第二个问题:
每次后缀推平,容易想到 颜色段均摊,因此可以开一个 来维护相同颜色段。
每个颜色段需要处理出一个二元组信息 ( x , f ) (x, f) (x,f) 表示把这个段前面的所有段都走过后当前的节点为 x x x f f f 表示是否已经经过叶子。然后可以对 t r i e trie trie 树每个节点 p p p 维护一个倍增数组 h p , c , i h_{p, c, i} hp,c,i 表示从 p p p 开始沿着 c c c 边连续走 2 i 2^i 2i 步会到那个节点,以及是否经过叶子。每次询问就二分出在哪一段然后跑一遍倍增即可。
对于修改,暴力弹栈加栈复杂度就是对的。

总复杂度 O ( n 3 + n 2 m + Q × log ⁡ ∣ R ∣ ) O(n^3 + n^2m + Q\times \log|R|) O(n3+n2m+Q×logR)

CODE:

// 分为两部分:
// 第一部分:求出AC自动机上每个点的期望值
// 这一部分的难点在于如何减少未知变量的个数
// 链剖分之
// 第二部分:如何维护快速查询一个前缀在AC自动机上的位置以及是否经过叶子
// 开一个栈维护连续颜色段倍增查询即可
#include<bits/stdc++.h>
#define MP make_pair
#define pb emplace_back
using namespace std;
typedef long long LL;
typedef pair< int, int > PII;
const LL mod = 1e9 + 7;
const int N = 105;
const int M = 1e4 + 10;
PII h[15][N * N][26];
int n, m, K, q;
int fail[N * N], tr[N * N][26], tot = 1;
int nxt[N * N];
char str[M];
bool leaf[N * N];
LL p[N], invp[N], inv, E[N * N];
void ins(char *str) {
	int len = strlen(str + 1); int p = 1;
	for(int i = 1; i <= len; i ++ ) {
		if(!tr[p][str[i] - 'a']) tr[p][str[i] - 'a'] = ++ tot;
		p = tr[p][str[i] - 'a'];
	}
	leaf[p] = 1; // 叶子
}
void dfs(int p, int lst) {
	bool f = 1;
	for(int i = 0; i < K; i ++ ) {
		if(tr[p][i]) {
			if(f) nxt[p] = tr[p][i], f = 0, dfs(tr[p][i], p);
			else dfs(tr[p][i], 0);
		}
	}
}
void build_AC() {
	queue< int > q; fail[1] = 1;
	for(int i = 0; i < K; i ++ ) {
		if(tr[1][i]) q.push(tr[1][i]), fail[tr[1][i]] = 1;
		else tr[1][i] = 1;
	}
	while(!q.empty()) {
		int u = q.front(); q.pop();
		for(int i = 0; i < K; i ++ ) {
			if(tr[u][i]) fail[tr[u][i]] = tr[fail[u]][i], q.push(tr[u][i]);
			else tr[u][i] = tr[fail[u]][i];
		}
	}
}
namespace expection {
	int r; // 表示当前的变量数
	LL f[N * N][N]; // 每个点对每个变量的系数  0 为常数
	LL g[N], val[N]; // val[i] 表示每个变量的真实值
	bool vis[N * N];
	inline LL Pow(LL x, LL y) {
		LL res = 1LL, k = x;
		while(y) {
			if(y & 1) res = res * k % mod;
			y >>= 1;
			k = k * k % mod;
		}
		return res;
	}
	struct Guess {
		int h;
		LL a[N][N]; 
		void init() {
			for(int i = 1; i <= tot; i ++ ) {
				if(leaf[i]) {
					h ++;
					for(int j = 1; j <= r; j ++ ) a[h][j] = f[i][j];
					a[h][r + 1] = (mod - f[i][0]) % mod;
				}
			}
		}
		void calc() {
			for(int i = 1; i <= r; i ++ ) { 
				int idx;
				for(int j = i; j <= h; j ++ ) 
					if(a[j][i]) {idx = j; break;}
				for(int j = 1; j <= r + 1; j ++ ) swap(a[i][j], a[idx][j]);
				LL inv = Pow(a[i][i], mod - 2LL);
				for(int j = 1; j <= r + 1; j ++ ) a[i][j] = a[i][j] * inv % mod;
				for(int j = 1; j <= h; j ++ ) {
					if(j == i) continue;
					LL p = a[j][i];
					for(int k = 1; k <= r + 1; k ++ ) a[j][k] = (a[j][k] - a[i][k] * p % mod + mod) % mod;
				}
			}
			for(int j = 1; j <= r; j ++ ) val[j] = a[j][r + 1];
		}
	} G;
	void bfs(int s) {
		r ++; f[s][r] = 1;
		queue< int > q; q.push(s); vis[s] = 1;
		while(!q.empty()) {
			int u = q.front(); q.pop(); // 假设 u 已经能表示出来了
			if(!nxt[u]) continue;
			for(int j = 0; j <= r; j ++ ) g[j] = f[u][j]; // 系数清空
			LL Inv;
			for(int i = 0; i < K; i ++ ) {
				if(!tr[u][i]) continue;
				if(tr[u][i] == nxt[u]) {Inv = invp[i]; continue;} // 在一个链上
				else if(!vis[tr[u][i]]) { // 没标记
					vis[tr[u][i]] = 1;
					r ++; f[tr[u][i]][r] = 1;
					q.push(tr[u][i]);
				}
				for(int j = 0; j <= r; j ++ ) g[j] = (g[j] - p[i] * f[tr[u][i]][j] % mod + mod) % mod;
			}
			g[0] = (g[0] - 1 + mod) % mod;
			for(int j = 0; j <= r; j ++ ) f[nxt[u]][j] = g[j] * Inv % mod;
			q.push(nxt[u]); vis[nxt[u]] = 1;
		}
	}
	void get() {
		for(int i = 0; i < K; i ++ ) inv = (inv + p[i]) % mod;
		inv = Pow(inv, mod - 2LL);
		for(int i = 0; i < K; i ++ ) p[i] = p[i] * inv % mod;
		for(int i = 0; i < K; i ++ ) invp[i] = Pow(p[i], mod - 2LL);
		bfs(1);
		G.init(); G.calc(); val[0] = 1;
		for(int i = 1; i <= tot; i ++ ) {
			LL res = 0;
			for(int j = 0; j <= r; j ++ ) res = (res + f[i][j] * val[j] % mod) % mod;
			E[i] = res;
		}
 	}
}
struct seg {
	int l, r, c; // 起点
	PII p; // 起点,是否经过叶子
};
int top;
seg stk[M];
PII jump(PII s, int k, int c) {
	int x = s.first, f = s.second;
	for(int i = 14; i >= 0; i -- ) {
		if(k >> i & 1) f |= h[i][x][c].second, x = h[i][x][c].first;
	}
	return MP(x, f);
}
void add(int l, int r, int c) {
	PII p = stk[top].p; int lc = stk[top].c;
	p = jump(p, stk[top].r - stk[top].l + 1, lc);
	stk[++ top] = (seg) {l, r, c, p};
}
void del(int l) {
	if(l <= stk[top].l) top --;
	else stk[top].r = l - 1;
}
LL ask(int x) {
	int l = 1, r = top, mid, res;
	while(l <= r) {
		mid = (l + r >> 1);
		if(stk[mid].r >= x) res = mid, r = mid - 1;
		else l = mid + 1;
	}
	PII p = jump(stk[res].p, x - stk[res].l + 1, stk[res].c);
	return p.second == 1 ? 0LL : E[p.first];
}
int main() {
	freopen("string.in", "r", stdin);
	freopen("string.out", "w", stdout);
	scanf("%d%d%d", &n, &m, &K);
	for(int i = 0; i < K; i ++ ) {
		scanf("%lld", &p[i]);
	}
	for(int i = 1; i <= n; i ++ ) {
		scanf("%s", str + 1);
		ins(str);
	}
	dfs(1, 0); // 划分出 n 条链
	build_AC();
	for(int i = 1; i <= tot; i ++ ) {
	    for(int j = 0; j < K; j ++ ) {
			h[0][i][j].first = tr[i][j];
			h[0][i][j].second |= leaf[tr[i][j]];
		}
	}
	for(int i = 1; i <= 14; i ++ ) {
		for(int j = 1; j <= tot; j ++ ) {
			for(int k = 0; k < K; k ++ ) {
				h[i][j][k].first = h[i - 1][h[i - 1][j][k].first][k].first;
				h[i][j][k].second = h[i - 1][j][k].second | h[i - 1][h[i - 1][j][k].first][k].second;
			}
		}
	}
	expection::get();
	scanf("%s", str + 1); int R = strlen(str + 1);
	stk[++ top] = (seg) {0, -1, 0, MP(1, 0)};
	for(int i = 1; i <= R; ) {
		int j = i;
		while(j <= R && str[j] == str[i]) j ++;
		add(i, j - 1, str[i] - 'a');
		i = j;
	}
	for(int i = 1; i <= R; i ++ ) {
		printf("%lld\n", i + ask(i));
	}
	scanf("%d", &q);
	for(int i = 1; i <= q; i ++ ) {
		int opt, l; char c;
		scanf("%d%d", &opt, &l); 
		if(opt == 1) scanf("\n%c", &c);
		if(opt == 1) {
			l = R - l + 1;
			while(stk[top].r >= l) del(l);
			add(l, R, c - 'a');
		}
		else printf("%lld\n", l + ask(l));
	}
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2304328.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32的“Unique device ID“能否修改?

STM32F1系列的"Unique device ID"寄存器的地址为0x1FFFF7E8。 这个寄存器是只读的。 "Unique device ID"寄存器位于“System memory”中。“System memory”地址范围为“0x1FFF F000- 0x1FFF F7FF”。 所有STM32 MCU上都存在系统引导加载程序。顾名思义&a…

[内网基础] 内网基础知识 —— Windows 工作组

关注这个专栏的其他相关笔记&#xff1a;[内网安全] 内网渗透 - 学习手册-CSDN博客 0x01&#xff1a;Windows 工作组介绍 在一个大型单位里&#xff0c;可能有成百上千台计算机互相连接组成局域网&#xff0c;如果不对这些计算机进行分组&#xff0c;网络的混乱程度是可想而知…

【新手初学】SQL注入之二次注入、中转注入、伪静态注入

二次注入 一、概念 二次注入可以理解为&#xff0c;攻击者构造的恶意数据存储在数据库后&#xff0c;恶意数据被读取并进入到SQL查询语句所导致的注入。 二、原理 防御者可能在用户输入恶意数据时对其中的特殊字符进行了转义处理&#xff0c;但在恶意数据插入到数据库时被处…

Deepseek存算分离安全部署手册

Deepseek大火后&#xff0c;很多文章教大家部署Dfiy和ollamadeepseek&#xff0c;但是大部分都忽略了数据安全问题&#xff0c;本文重点介绍Deepseek存算分裂安全架设&#xff0c;GPU云主机只负责计算、CPU本地主机负责数据存储&#xff0c;确保数据不上云&#xff0c;保证私有…

单页图床HTML源码+本地API接口图床系统修复版源码

源码介绍 图床系统是一种用于存储和管理图片文件的在线服务。它允许用户上传图片文件&#xff0c;并生成相应的图片链接&#xff0c;从而方便用户在网页、社交媒体或其他平台上分享图片。 PS:源码压缩包分为两个版本&#xff0c;一个是调用360第三方api接口&#xff0c;另外一…

IDEA使用Maven方式构建SpringBoot项目

1、环境准备 确保你已经安装了以下工具&#xff1a; Java JDK&#xff08;推荐 JDK 8 或更高版本&#xff09; IntelliJ IDEA&#xff08;推荐使用最新版本&#xff09; 2、创建 Spring Boot 项目 &#xff08;1&#xff09; 打开 IntelliJ IDEA。 &#xff08;2&#xff09…

【SPIE出版,见刊快速,EI检索稳定,浙江水利水电学院主办】2025年物理学与量子计算国际学术会议(ICPQC 2025)

2025年物理学与量子计算国际学术会议&#xff08;ICPQC 2025&#xff09;将于2025年4月18-20日在中国杭州举行。本次会议旨在汇聚全球的研究人员、学者和业界专家&#xff0c;共同探讨物理学与量子计算领域的最新进展与前沿挑战。随着量子技术的快速发展&#xff0c;其在信息处…

查看cmd下python的安装路径 + Windows 命令行添加环境变量和不重启刷新环境变量

1、查看cmd下python的安装路径 cmd ----》输入“python” 命令 ---》输入 import sys; print(sys.executable) 即可看到当前系统python的安装路径&#xff1a; 注&#xff1a;系统所使用的python实际上就是在系统环境变量下配置的python目录。 2、刷新path命令&#xff1a;在c…

C/C++跳动的爱心

系列文章 序号直达链接1C/C李峋同款跳动的爱心2C/C跳动的爱心3C/C经典爱心4C/C满屏飘字5C/C大雪纷飞6C/C炫酷烟花7C/C黑客帝国同款字母雨8C/C樱花树9C/C奥特曼10C/C精美圣诞树11C/C俄罗斯方块小游戏12C/C贪吃蛇小游戏13C/C孤单又灿烂的神14C/C闪烁的爱心15C/C哆啦A梦16C/C简单…

Cannot deserialize instance of java.lang.String out of START_ARRAY token

这个错误 Cannot deserialize instance of java.lang.String out of START_ARRAY token 表示 Jackson 正在尝试将一个 JSON 数组反序列化成一个 String 类型的字段&#xff0c;但是 JSON 中传递的是一个数组而不是单一的字符串。 具体来说&#xff0c;这段堆栈信息&#xff1a…

一、初始爬虫

1.爬虫的相关概念 1.1 什么是爬虫 网络爬虫&#xff08;又被称为网页蜘蛛&#xff0c;网络机器人&#xff09;就是模拟浏览器发送网络请求&#xff0c;接收请求响应&#xff0c;一种按照一定的规则&#xff0c;自动地爬取互联网信息的程序。 原则上&#xff0c;只要是浏览器…

在低功耗MCU上实现人工智能和机器学习

作者&#xff1a;Silicon Labs 人工智能&#xff08;AI&#xff09;和机器学习&#xff08;ML&#xff09;技术不仅正在快速发展&#xff0c;还逐渐被创新性地应用于低功耗的微控制器&#xff08;MCU&#xff09;中&#xff0c;从而实现边缘AI/ML解决方案。这些MCU是许多嵌入式…

QQ登录测试用例报告

QQ登录测试用例思维导图 一、安全性测试用例 1. 加密传输与存储验证 测试场景&#xff1a;输入账号密码并提交登录请求。预期结果&#xff1a;账号密码通过加密传输&#xff08;如HTTPS&#xff09;与存储&#xff08;如哈希加盐&#xff09;&#xff0c;无明文暴露。 2. 二…

细说STM32F407单片机2个ADC使用DMA同步采集各自的1个输入通道的方法

目录 一、示例说明 二、工程配置 1、RCC、DEBUG、CodeGenerator 2、USART6 3、TIM3 &#xff08;1&#xff09;Mode &#xff08;2&#xff09;参数设置 &#xff08;3&#xff09; TRGO &#xff08;4&#xff09;ADC1_IN0 1&#xff09;ADCs_Common_Settings 2&a…

[python脚本]论文1.(一)CPU/内存数据分析和分组

CPU 收集到的CPU数据&#xff0c;格式如下&#xff1a; 由于这里6个数据为一组来收集latency的数据以及各个分位值的数据&#xff0c;而本质上每一行都是一次完整的测试&#xff0c;因此这里将这个csv文件分为两个文件&#xff0c;第一个是和latency相关的&#xff0c;将6条数…

一周学会Flask3 Python Web开发-Jinja2模板基本使用

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 我们平台开发web系统&#xff0c;必须用到网页&#xff0c;单纯的静态网页无法满足我们的需求。我们可以使用模版引擎技术&am…

《操作系统 - 清华大学》8 -3:进程管理:进程特点

深入剖析进程的特点、实现及进程控制块 一、进程的特点 &#xff08;一&#xff09;动态性 进程具有明显的动态性。它可以被动态创建&#xff0c;在执行过程中会发生状态切换&#xff0c;从一个状态转变为另一个状态。当所有任务执行完毕后&#xff0c;进程还会结束运行。整…

Java 大视界 -- 总结与展望:Java 大数据领域的新征程与无限可能(96)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

【微服务】深入解析spring aop原理

目录 一、前言 二、AOP 概述 2.1 什么是AOP 2.2 AOP中的一些概念 2.2.1 aop通知类型 2.3 AOP实现原理 2.3.1 aop中的代理实现 2.4 静态代理与动态代理 2.4.1 静态代理实现 三、 jdk动态代理与cglib代理 3.1 jdk动态代理 3.1.1 jdk动态代理模拟实现 3.2 CGLIB 代理…

pikachu靶场搭建教程

需要的东西 phpStudy&#xff1a; 链接&#xff1a; https://pan.baidu.com/s/1fJ-5TNtdDZGUf5FhTm245g 提取码&#xff1a;0278 pikachu-master&#xff1a; Github链接&#xff1a;https://github.com/zhuifengshaonianhanlu/pikachu 链接&#xff1a; https://pan.baidu.c…