【USACO 23JAN Gold】 Find and Replace

文章介绍了一种方法来解决关于字符序列在限定次数变换后的新状态问题。通过构建树形结构并进行预处理,可以优化查询过程,将时间复杂度降低到O(n)。在处理过程中,注意到树上的同构子树和长链可能导致效率降低,通过适当优化,如等价代换树的结构,可以进一步提高算法性能。
摘要由CSDN通过智能技术生成

题目链接

点击打开链接

题目解法

我们可以把求 l − r l-r lr 的问题暂时简化成单点的问题
我们发现如果把替换建成树的结构的话
虽然这颗树可能很大,但我们可以假定它已经建了出来
我们考虑如何缩小这棵树的大小
可以发现这棵树上有很多的子树是同构的,可以考虑这些子树只记录一次即可
我们发现在 t t t 次询问后字符 c c c 可以变成的序列是确定的
那么可以记 n e [ t ] [ c ] ne[t][c] ne[t][c] 表示在第 t t t 次询问时字母是 c c c,下一次改变 c c c 的值会在何处
n e [ t ] [ c ] ne[t][c] ne[t][c] 可以通过倒序预处理出来
所以我们可以间接地建出这张图
然后以同样的方法预处理出 s u m [ t ] [ c ] sum[t][c] sum[t][c] 就可以在 O ( n ) O(n) O(n) 的时间内求出第 k k k 个点变化后的字符

转化成 [ l , r ] [l,r] [l,r] 的问题,可以发现是连续的,那么在树上的叶子也是连续的
然后可以一个一个输出

上面的方法在节点有分叉的时候是没有错的,因为一个分叉至少会带来一个叶子
但我们发现可能会有这样的情况:
请添加图片描述
有一条很长的链会导致运行很多次都不能添加新的解
这里可以在运行完第一次这条链后就把这条链上每个点连到链的最后一个点
相当于等价代换树的结构
时间复杂度大概是预处理的 O ( 26 n ) O(26n) O(26n)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N(200100),inf(2e18);
int now,r,n,len[N];
int last[N],ne[N][26],sum[N][26];
char c[N];
string s[N];
inline int read(){
	int FF=0,RR=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
	for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
	return FF*RR;
}
void print(int dep,int c,int tot){
	if(dep>n){
		now++,putchar(c+'a');
		if(now>r) exit(0);
		return;
	}
	if(len[dep]==1){
		int _ne=ne[dep][s[dep][0]-'a'];
		print(_ne,s[dep][0]-'a',tot);
		if(len[_ne]==1){
			s[dep][0]=s[_ne][0];
			ne[dep][s[dep][0]-'a']=ne[_ne][s[dep][0]-'a'];
		}
	}
	for(int i=0;i<len[dep];i++){
		int _c=s[dep][i]-'a';
		int w=min(inf,tot+sum[ne[dep][_c]][_c]);
		while(w>=now) print(ne[dep][_c],_c,tot);
		tot=w;
	}
}
signed main(){
	now=read(),r=read(),n=read();
	for(int i=1;i<=n;i++)
		cin>>c[i]>>s[i],len[i]=s[i].size();
	for(int i=0;i<26;i++) last[i]=n+1,sum[n+1][i]=1ll;
	for(int i=n;i;i--){
		for(int j=0;j<26;j++)
			ne[i][j]=last[j];
		last[c[i]-'a']=i;
	}
	for(int i=n;i;i--){
		for(int j=0;j<26;j++)
			sum[i][j]=sum[i+1][j];
		sum[i][c[i]-'a']=0;
		for(int j=0;j<len[i];j++)
			sum[i][c[i]-'a']=min(inf,sum[i][c[i]-'a']+sum[i+1][s[i][j]-'a']);
	}
	print(last[0],0,0);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值