JOISC2022 复制粘贴(区间DP,字符串hash)

题目描述

题面

在这里插入图片描述

分析

       这道题考场没有任何头绪,赛后也是看了许多题解才明白状态设计和转移的一步步思考过程。

       首先我们需要想到 无论是屏幕上的字符串,还是剪切板上的字符串,在任何时候都必须是目标串的子串。这个非常好像,如果不是目标串的子串,那么后期一定要再花费代价将它们改回来,这样一定不是最优的。因此,基于这个性质,我们可以设出一个四维状态: d p l x , r x , l y , r y dp_{l_x,r_x,l_y,r_y} dplx,rx,ly,ry 表示当前屏幕上的串是目标串的 [ l x , r x ] [l_x,r_x] [lx,rx] 区间,剪切板上的串是目标串的 [ l y , r y ] [l_y, r_y] [ly,ry] 区间的最小代价。

       那么显然有转移:

  • d p l x , r x , l y , r y + A → d p l x − 1 , r x , l y , r y dp_{l_x, r_x, l_y, r_y} + A \to dp_{l_x - 1,r_x,l_y,r_y} dplx,rx,ly,ry+Adplx1,rx,ly,ry d p l x , r x , l y , r y + A → d p l x , r x + 1 , l y , r y dp_{l_x,r_x,l_y,r_y} + A \to dp_{l_x,r_x + 1,l_y, r_y} dplx,rx,ly,ry+Adplx,rx+1,ly,ry
  • d p l x , r x , l y , r y + B → d p l x , r x + ( r y − l y + 1 ) dp_{lx, rx, ly, ry} + B \to dp_{l_x, r_x +(r_y - l_y + 1)} dplx,rx,ly,ry+Bdplx,rx+(ryly+1) (当且仅当 [ S r x + 1 , S r x + ( r y − l y + 1 ) ] = [ S l y , S r y ] [S_{rx + 1}, S_{rx + (ry - ly + 1)}] = [S_{ly}, S_{ry}] [Srx+1,Srx+(ryly+1)]=[Sly,Sry] S S S 为目标串)。②
  • d p l x , r x , l y , r y + C → d p 0 , 0 , l x , r x dp_{lx, rx, ly, ry} + C \to dp_{0, 0, lx, rx} dplx,rx,ly,ry+Cdp0,0,lx,rx

       这样的四维状态复杂度过高, l x l_x lx r x r_x rx 显然去不掉,我们考虑怎样去掉 l y l_y ly r y r_y ry 这两维。我们尝试 合并 第一个转移和第二个转移。也就是说,我们将转移修改为 对于一个串,先进行一次③操作,然后进行若干次①操作②操作,顺序随意得到一个新串。那么我们就得到了一个新的状态设计和转移:

       设 d p l , r dp_{l, r} dpl,r 表示得到屏幕上的串为 [ S l , S r ] [S_l, S_r] [Sl,Sr] 的最小代价。并且规定下一步转移要先剪切,再通过若干次添加和复制得到新串。 那么答案就是 d p 1 , n dp_{1, n} dp1,n

       接着,我们考虑转移:

  • d p l , r + A → d p l − 1 , r dp_{l, r} + A \to dp_{l - 1, r} dpl,r+Adpl1,r d p l , r + A → d p l , r + 1 dp_{l, r} + A \to dp_{l, r + 1} dpl,r+Adpl,r+1。①
  • d p l , r + B + C × k + A × ( r − p + 1 − k × ( r − l + 1 ) ) → d p p , r dp_{l, r} + B + C \times k + A \times (r - p + 1 - k \times(r - l + 1)) \to dp_{p, r} dpl,r+B+C×k+A×(rp+1k×(rl+1))dpp,r(需要满足 [ S p , S r ] [S_p, S_r] [Sp,Sr] 中最多有 k k k 个不想交 [ S l , S r ] [S_l, S_r] [Sl,Sr] p p p 是满足这个条件最靠右的位置)。②

       这里解释一下几个问题:

       Q 1 : Q_1: Q1: 第一个转移的含义不是在 [ l , r ] [l, r] [l,r] 上直接添加字符得到新串吗?这样不是跟状态的定义不相符吗?

       A 1 : A_1: A1: 这个转移是必要的,它虽然与我们的定义稍有不符,但这个转移一定没有错的。我们加上它可以覆盖到所有情况,不加的话会有情况取不到最优值。

       Q 2 : Q_2: Q2: 为什么转移的时候右端点不用变? d p l , r dp_{l, r} dpl,r 不是也能够往 右端点向右移的区间转移吗?

       A 2 : A_2: A2 我们考虑如果右端点右移得到新区间的右端点为 q q q,那么如果 [ S r , S q ] [S_r, S_q] [Sr,Sq] 不包含 [ S l , S r ] [S_l, S_r] [Sl,Sr],那么它一定可以由 d p l , r dp_{l, r} dpl,r 通过若干步①转移过来。这也说明了①转移的必要性。如果 [ S r , S q ] [S_r, S_q] [Sr,Sq] 包含 [ S l , S r ] [S_l, S_r] [Sl,Sr] 那么它一定可以有更靠右的和 [ S l , S r ] [S_l, S_r] [Sl,Sr] 相等的字符串区间由②转移的到。因此只用改变左端点就好了。

       我们还需要解决一个问题,就是转移时如何求出下一个 p p p。我们可以预处理一个 p r e l , r pre_{l, r} prel,r 数组,表示 [ l , r ] [l, r] [l,r]字符串相等,并且与 [ l , r ] [l, r] [l,r]不交的最靠右的区间 [ p , q ] [p, q] [p,q] 的右端点,也就是 p r e [ l , r ] pre[l, r] pre[l,r] 的值为 q q q。我们首先进行 字符串hash,那么 hash 值的种类最多 n 2 n^2 n2 个,我们对每一种 h a s h hash hash 值开一个 v e c t o r vector vector 存 对应区间的右端点。那么 p r e pre pre 数组就可以在 v e c t o r vector vector 里面二分求解。

       时间复杂度 O ( n 2 ln ⁡ n ) O(n^2\ln n) O(n2lnn)

CODE:

#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef unsigned long long ull;
typedef long long LL;
const int N = 2550;
const int M = N * N;
map< ull, int > mp;
vector< int > pos[M];
char str[N];
struct Hash {
	ull val; int rk, last;
}h[M];
int n, rk, pre[N][N], head[M], tot; // pre[l][r] 表示 l 前面第一个与 [Sl, Sr] 相等的字符串的右端点,并且没有交集
ull base = 1331, S[N], b[N], p = 1000037;
LL A, B, C, dp[N][N]; // dp[l][r]表示生成 [l, r] 的最小值 
ull calc(int l, int r) {
	return S[r] - S[l - 1] * b[r - l + 1];
}
int Find(int idx, int c) {
	int l = 0, r = pos[idx].size() - 1, mid, res = 0;
	while(l <= r) {
		mid = (l + r >> 1);
		if(pos[idx][mid] <= c) res = pos[idx][mid], l = mid + 1;
		else r = mid - 1;
	}
	return res;
}
void ins(ull val, int rk) {
	ull k = val % p;
	h[++ tot].last = head[k];
	h[tot].val = val;
	h[tot].rk = rk;
	head[k] = tot;
}
int GET(ull val) {
	ull k = val % p;
	for(int i = head[k]; i; i = h[i].last) {
		if(h[i].val == val) return h[i].rk;
	}
	return 0;
}
void get_pre() {
	for(int r = 1; r <= n; r ++ ) 
		for(int l = 1; l <= r; l ++ ) 
			if(!GET(calc(l, r))) ins(calc(l, r), ++rk);
	for(int r = 1; r <= n; r ++ ) {
		for(int l = 1; l <= r; l ++ ) {
			ull val = calc(l, r);
			pre[l][r] = Find(GET(val), l - 1);
			pos[GET(val)].pb(r);
		}
	}
}
void DP() {
	memset(dp, 0x3f, sizeof dp);
	for(int i = 1; i <= n; i ++ ) dp[i][i] = A;
	for(int len = 1; len < n; len ++ ) {
		for(int L = 1; L + len - 1 <= n; L ++ ) {
			int R = L + len - 1; // dp[L][R]
			dp[L][R + 1] = min(dp[L][R + 1], dp[L][R] + A);
			dp[L - 1][R] = min(dp[L - 1][R], dp[L][R] + A);
			int p = pre[L][R];
			LL cnt = 1;
			while(p) {
				int lp = p - len + 1;
				cnt ++;
				dp[lp][R] = min(dp[lp][R], dp[L][R] + B + cnt * C + (1LL * R - lp + 1 - cnt * len) * A);
				p = pre[lp][p];
			}
		}
	}
	printf("%lld\n", dp[1][n]);
}
int main() {
	scanf("%d", &n);
	scanf("%s", str + 1);
	scanf("%lld%lld%lld", &A, &B, &C);
	b[0] = 1;
	for(int i = 1; i <= n; i ++ ) b[i] = b[i - 1] * base;
	for(int i = 1; i <= n; i ++ ) {
		S[i] = S[i - 1] * base + (ull)(str[i]);
	}
	get_pre();
	DP();
	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

视频汇聚/存储/压缩/诊断平台EasyCVR视频联网整合方案应用特点

随着科技的不断发展&#xff0c;监控视频在各个领域的应用越来越广泛。为了更好地管理和利用这些视频资源&#xff0c;视频联网与整合的需求也越来越多。通过视频联网技术将不同地理位置或不同设备的视频资源进行整合&#xff0c;实现实时共享和集中管理。视频联网整合方案的应…

快速创建百度百科,打造专属品牌词条

本文迅推客传媒将为小白详细讲解如何创建百度百科&#xff0c;并提供详细的教程。 创建百度百科账号 请先注册一个。注册完成后&#xff0c;登录百度账号。在搜索框中输入“百度百科”&#xff0c;进入百度百科官网。 选择创建词条类型 根据自己的需要选择相应的分类。例如&…

Outlook邮箱IMAP怎么开启?服务器怎么填?

Outlook邮箱IMAP服务器如何开启&#xff1f;Outlook设置IMAP的方法&#xff1f; Outlook邮箱作为其中的佼佼者&#xff0c;被广大用户所青睐。但在使用Outlook邮箱时&#xff0c;许多用户可能会碰到一个问题&#xff1a;如何开启IMAP服务&#xff1f;下面&#xff0c;蜂邮EDM就…

学习大数据,所必需的java基础(6)

文章目录 集合Set集合介绍HashSet集合的介绍和使用LinkedHashSet的介绍以及使用哈希值哈希值的计算方式HashSet的存储去重的过程 Map集合Map的介绍HashMap的介绍以及使用HashMap的两种遍历方式方式1&#xff1a;获取key&#xff0c;然后再根据key获取value方式2&#xff1a;同时…

trie树(前缀树)

前缀树 1. 前缀树的的介绍2.前缀树的实现2.1插入功能2.2删除功能2.3查找前缀和查找单词功能2.4 哈希表版本 1. 前缀树的的介绍 在计算机科学中&#xff0c;trie&#xff0c;又称前缀树或字典树&#xff0c;是一种有序树&#xff0c;用于保存关联数组&#xff0c;其中的键通常是…

《Spring Security 简易速速上手小册》第1章 Spring Security 概述(2024 最新版)

文章目录 1.1 Spring Security 的重要性1.1.1 基础知识详解1.1.2 主要案例&#xff1a;用户认证与授权1.1.3 拓展案例 1&#xff1a;OAuth2 社交登录1.1.4 拓展案例 2&#xff1a;JWT 认证 1.2 Spring Security 的核心特性1.2.1 基础知识详解1.2.2 主要案例&#xff1a;基于角色…

11.网络游戏逆向分析与漏洞攻防-游戏网络架构逆向分析-接管游戏接收网络数据包的操作

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;接管游戏发送数据的操作 码云地址&#xff08;master 分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/titan 码云版本号&#xff1a;8256eb53e8c16281bc1a29cb8d26d352bb5bbf4c 代…

Android Duplicate class 排除重复类

一、起因&#xff1a; 在迭代开发的时候&#xff0c;发现2个ijk很多类重复。但又2个库实现的功能是不一样&#xff0c;目前不能合并。但又想保留2个功能。需要排除其中一个库。 二、报错如何下图&#xff1a; 三、解决方法&#xff1a; 3.1 在terminal 也就是命令行处输入 …

js 面试 什么是WebSockets?HTTP和HTTPS有什么不同?web worker是什么?

概念&#xff1a; webSocket 是一种在客户端和服务端之间建立持久连接的协议&#xff0c;它提供全双工通信通道&#xff0c;是服务器可以主动向客户端推送数据&#xff0c;同时也可以接受客户端发送的数据。 1 webSocket与https区别&#xff1f; 在网络通信中&#xff0c;We…

【mysql版本修改】

1、使用telnet确认当前mysql版本号 telnet <MySQL服务器IP地址> <MySQL端口号> telnet 192.168.38.20 33062、使用strings查看/usr/sbin/mysqld中包含版本号的字符串 # 查看/usr/sbin/mysqld文件中是否包含对应的版本号 strings /usr/sbin/mysqld | grep 5.7.30 …

Unity | 动态读取C#程序集实现热更新

目录 一、动态语言 二、创建C#dll 1.VS中创建一个C#语言的库工程 2.添加UnityEngine.dll的依赖 3.编写代码&#xff0c;生成dll 三、Unity使用dll 一、动态语言 计算机编程语言可以根据它们如何将源代码转换为可以执行的代码来分类为静态语言和动态语言。 静态语言&…

Spark Bloom Filter Join

1 综述 1.1 目的 Bloom Filter Join&#xff0c;或者说Row-level Runtime Filtering&#xff08;还额外有一条Semi-Join分支&#xff09;&#xff0c;是Spark 3.3对运行时过滤的一个最新补充   之前运行时过滤主要有两个&#xff1a;动态分区裁剪DPP&#xff08;开源实现&am…

ISO_IEC_18598-2016自动化基础设施管理(AIM)系统国际标准解读(一)

██ ISO_IEC_18598-2016是什么标准&#xff1f; ISO/IEC 18598国际标准是由ISO&#xff08;国际标准化组织&#xff09;/IEC&#xff08;国际电工委员会&#xff09;联合技术委员会1-信息技术的第25分委员会-信息技术设备互连小组制定的关于信息基础设施自动化管理的国际标准&…

C++中atomic的使用

atomic使用 概述介绍使用场景头文件atomic的使用创建load()store()exchange()compare_exchange_weakcompare_exchange_strong&#xff08;&#xff09;fetch_add()fetch_sub()fetch_and()fetch_or()fetch_xor() 示例实现代码运行结果 概述 本文只要讲述C11中atomic的使用&…

一文读懂Prometheus和Grafana的区别(适合小白)

Prometheus和Grafana是两种开源软件&#xff0c;分别用于监控和可视化数据。它们的主要功能和特点如下&#xff1a; Prometheus 监控系统&#xff1a;Prometheus是一个专门用于收集和存储时间序列数据的监控系统。它可以从各种目标&#xff08;如服务器、数据库等&#xff09…

Spring中的事务和事务的传播机制

事务是一组操作的集合&#xff0c;不可以被分割。事务会把所有的操作作为一个整体&#xff0c;这组操作要么全部成功&#xff0c;要么全部失败。 事务有三种操作&#xff1a; 开启事务&#xff1b;提交事务&#xff1b;回滚事务。 如果代码的执行逻辑是这样&#xff1a; 开…

NutUI + taro +vue 开发遇到的问题 使用popup组件 内部元素滚动遇到的的问题

1 popup 弹出内容时 弹出的框内元素数据很长需要滚动时 本地可以正常滚动 打包成小程序后无法滚动 如这样的免责条款内容 代码如下 解决办法 1 把2处的单位换成百分比 弹框能滚动但是 是popup 里面所有的元素都一起滚动 导致标题都滚走了 2 scroll-y 改成&#xff1a; :scrol…

Excel数据表定制分组排序

实例需求&#xff1a;某学校体育活动统计表如下图左侧表格所示&#xff0c;数据按照班级排列&#xff0c;现在需要根据如下规格对表格进行排序 “幼儿”班级排列在表格最后按照“次数”降序排列“幼儿”班级同样按“次数”降序排列 排序结果如下图中右侧表格所示。 示例代码…

GDB之(8)GDB-Server远程调试

GDB之(8)GDB-Server远程调试 Author&#xff1a;Once Day Date&#xff1a;2024年2月27日 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章请查看专栏: Linux实践记录_Once-Day的博客-CSDN博客 参考文档: 用GDB调试程序 _CSDN博客 _陈皓GDB: The GNU Project Debugger…

UE4c++ ConvertActorsToStaticMesh ConvertProceduralMeshToStaticMesh

UE4c ConvertActorsToStaticMesh 创建Edior模块&#xff08;最好是放Editor模块毕竟是编辑器代码&#xff09;创建蓝图函数UBlueprintFunctionLibraryUTestFunctionLibrary.hUTestFunctionLibrary.cpp:.Build.cs 目标:为了大量生成模型&#xff0c;我们把虚幻带有的方法迁移成函…