CF 935E - Fafa and Ancient Mathematics

2023-11-17

CF 935E - Fafa and Ancient Mathematics

题目描述

定义合法数学表达式 E E E 为一个数或两个合法数学表达式中间加上一个加或减运算符,并且在外面加上一对括号。

给定一个合法数学表达式,将其中加减运算符用 ? ? ? 待定,最多指派 p p p 个加法运算符和 q q q 次减法运算(保证 p + q =   ? p + q = \ ? p+q= ? 的个数),求给定的数学表达式的最大值。

数据范围

1 ≤ ∣ E ∣ ≤ 1 ⋅ 1 0 4 , 1 ≤ m i n ( p , q ) ≤ 100 1 \leq |E| \leq 1 \cdot 10^4, 1 \leq min (p, q) \leq 100 1E1104,1min(p,q)100


题解报告

首先可以构造一棵树来表达各个表达式之间的运算关系。

具体来说,树的每棵子树表示一个合法数学表达式,

叶子节点表示一个具体的数(也是合法数学表达式,与子树的含义不冲突),

每个非叶子节点仅有两个儿子节点(因为一个加法或减法运算符只能连接两个数学表达式),

并且需要指派一个加法或减法运算符,表示通过该运算符直接将左子树代表的数学表达式与右子树代表的数学表达式进行一次加法或减法运算。构造过程类似于 K r u s k a l Kruskal Kruskal 重构树。

至于实现方式,可以用一个栈来保存最新的数学表达式,

从左到右遍历给定的数学表达式时,遇到一个数字就把他放入栈中;

遇到一个 " ) " ")" ")" 就在树上新建一个节点,左儿子和右儿子为栈顶两个元素在树上对应的节点;

弹出栈顶两个元素,同时把这个新的节点也放入栈中,两个数学表达式合并成了一个。

利用这种方法,遍历完给定的数学表达式后,栈中一定只剩下一个元素,且该元素正好是构造的树的根节点编号。

构造完这棵树后,考虑在这棵树上进行树上 d p dp dp

定义 m x [ i ] [ j ] mx[i][j] mx[i][j] 表示第 i i i 个节点所代表的子树内使用 j j j 次加法运算符的最大运算结果,

m n [ i ] [ j ] mn[i][j] mn[i][j] 表示第 i i i 个节点所代表的子树内使用 j j j 次加法运算符的最大运算结果,

对于每个叶子节点,有 $mx[i][0] = mn[i][0] = $ 该叶子节点代表的具体的数;

对于每个非叶子节点,

  • 当指派节点 i i i 为加法运算符时,考虑 m x [ i ] [ j ] mx[i][j] mx[i][j] m n [ i ] [ j ] mn[i][j] mn[i][j] 状态如何转移。

    暴力枚举左子树使用加法运算符次数 k 1 k_1 k1 0 0 0 j − 1 j - 1 j1

    右子树使用加法运算符次数 k 2 k2 k2 满足 k 1 + k 2 + 1 = j k1 + k2 + 1 = j k1+k2+1=j,即 k 2 = j − k 1 − 1 k2 = j - k1 - 1 k2=jk11

    于是有 m x [ i ] [ j ] = m a x k 1 = 0 , 1... j − 1 ( m x [ l s ] [ k 1 ] + m x [ r s ] [ j − k 1 − 1 ] ) mx[i][j] = \underset{k1 = 0,1 ...j - 1} {max} (mx[ls][k1] + mx[rs][j - k1 - 1]) mx[i][j]=k1=0,1...j1max(mx[ls][k1]+mx[rs][jk11])

    其中 l s ls ls r s rs rs 分别表示节点 i i i 的左儿子和右儿子;

    类似的有 m n [ i ] [ j ] = m i n k 1 = 0 , 1... j − 1 ( m n [ l s ] [ k 1 ] + m n [ r s [ j − k 1 − 1 ] ) mn[i][j] = \underset {k1 = 0, 1...j - 1}{min} (mn[ls][k1] + mn[rs[j - k1 - 1]) mn[i][j]=k1=0,1...j1min(mn[ls][k1]+mn[rs[jk11])

  • 当指派节点 i i i 为减法运算符时, m x [ i ] [ j ] mx[i][j] mx[i][j] m n [ i ] [ j ] mn[i][j] mn[i][j] 状态的转移过程与上述类似,

    主要区别是转移方程应修改为:(注意枚举上限不同了)

    m x [ i ] [ j ] = m a x k 1 = 0 , 1... j ( m x [ l s ] [ k 1 ] − m n [ r s ] [ j − k 1 − 1 ] ) mx[i][j] = \underset{k1 = 0,1 ...j} {max} (mx[ls][k1] - mn[rs][j - k1 - 1]) mx[i][j]=k1=0,1...jmax(mx[ls][k1]mn[rs][jk11])

    m n [ i ] [ j ] = m i n k 1 = 0 , 1... j ( m n [ l s ] [ k 1 ] − m x [ r s [ j − k 1 − 1 ] ) mn[i][j] = \underset {k1 = 0, 1...j}{min} (mn[ls][k1] - mx[rs[j - k1 - 1]) mn[i][j]=k1=0,1...jmin(mn[ls][k1]mx[rs[jk11])

由于转移过程是 O ( p ) O(p) O(p) 的,每个节点有 p p p 种状态,所以复杂度是 O ( n p 2 ) O(np^2) O(np2)

题目中还有个限制条件: m i n ( p , q ) ≤ 100 min(p, q) \leq 100 min(p,q)100

p ≤ 100 p \leq 100 p100 时,上述做法显然不会 T L E TLE TLE

当仅有 q ≤ 100 q \leq 100 q100 时,只需把状态设置成减法运算符的使用次数,并对转移方式做细节上的修改即可。

所以考虑限制条件后的复杂度应为 O ( n ⋅ m i n ( p , q ) 2 ) O(n \cdot min (p, q)^2) O(nmin(p,q)2)

AC代码:

#include <bits/stdc++.h>
#define ll long long
#define int ll
using namespace std;
const int N = 1e5 + 5;
const int M = 1e2 + 5;

int n, p, q, tot;

void init () {}

void charming () {
	init ();
	string s;
	cin >> s, n = s.size ();
	cin >> p >> q;	
	vector <int> isLeaf (N), siz (N);
	vector <array <int, 2> > ch (N);
	vector <vector <int> > mx (N, vector <int> (M, -INT_MAX));
	vector <vector <int> > mn (N, vector <int> (M, INT_MAX));
	vector <int> stk;
	
	for (int i = 0, d, lid, rid; i < n; ++i) {
		if (s[i] == '(') {
			continue;
		}
		else if (s[i] == '?') {
			continue;
		}
		else if (s[i] == ')') {
			++tot;
			rid = stk.back ();
			stk.pop_back ();
			lid = stk.back ();
			stk.pop_back ();
			ch[tot][0] = lid, ch[tot][1] = rid;
			siz[tot] = siz[lid] + siz[rid] + 1;
			stk.emplace_back (tot);
		}
		else {
			++tot;
			isLeaf[tot] = true;
			d = s[i] - '0';
			mn[tot][0] = mx[tot][0] = d;
			stk.emplace_back (tot);
		}
	}
	
	auto DFS = [&] (auto && me, int cur) -> void {
		if (isLeaf[cur]) return;
		int &lid = ch[cur][0], &rid = ch[cur][1];
		me (me, lid), me (me, rid);
		
		if (p <= 100) {
			for (int i = 1; i <= min (siz[cur], p); ++i) {
				for (int j = max (0ll, i - 1 - siz[rid]); j <= min (i - 1, siz[lid]); ++j) {
					mx[cur][i] = max (mx[cur][i], mx[lid][j] + mx[rid][i - 1 - j]);
					mn[cur][i] = min (mn[cur][i], mn[lid][j] + mn[rid][i - 1 - j]);
				}
			}
			for (int i = 0; i <= min (siz[cur], p); ++i) {
				for (int j = max (0ll, i - siz[rid]); j <= min (i, siz[lid]); ++j) {
					mx[cur][i] = max (mx[cur][i], mx[lid][j] - mn[rid][i - j]);
					mn[cur][i] = min (mn[cur][i], mn[lid][j] - mx[rid][i - j]);
				}
			}
		}
		else {
			for (int i = 0; i <= min (siz[cur], q); ++i) {
				for (int j = max (0ll, i - siz[rid]); j <= min (i, siz[lid]); ++j) {
					mx[cur][i] = max (mx[cur][i], mx[lid][j] + mx[rid][i - j]);
					mn[cur][i] = min (mn[cur][i], mn[lid][j] + mn[rid][i - j]);
				}
			}
			for (int i = 1; i <= min (siz[cur], q); ++i) {
				for (int j = max (0ll, i - 1 - siz[rid]); j <= min (i - 1, siz[lid]); ++j) {
					mx[cur][i] = max (mx[cur][i], mx[lid][j] - mn[rid][i - 1 - j]);
					mn[cur][i] = min (mn[cur][i], mn[lid][j] - mx[rid][i - 1 - j]);
				}
			}
		}
	};
	
	int rt = stk[0];
	DFS (DFS, rt);
	if (p <= 100) cout << mx[rt][p] << endl;
	else cout << mx[rt][q] << endl;
}

signed main () {
	charming ();
	return 0;
}

后记

这个题目其实主要分两个部分,建树和 d p dp dp,然后两个部分其实都不是很难, r a t i n g rating rating 却有 2300 2300 2300。拿来给今年集训队 d i v 2 div2 div2 的训,做出来的人数还可以吧。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CF 935E - Fafa and Ancient Mathematics 的相关文章

  • const int & a = 1;

    int a 1 报错 引用需要一个合法的内存空间 const int a 1 正确 类似于int temp 1 const int a temp
  • String时间类型转换为ZonedDateTime时间类型

    搞了一个早上 不知道怎么弄这个东西 最后发现没有必要将ZonedDateTime写的很全 可以精简的封装 public static ZonedDateTime changeShanghaiToUTC String beijingDateT

随机推荐

  • 【MIUI9】小米平板1MIPAD1欧版ROM历史ROM下载地址-另附挥泪典藏版V9系统

    费劲整理来的 上边是 小米平板1 MIPAD1的 ROM 下边是MI3W小米3联通版的ROM 欧版xiaomi eu系统的好处就是省电 miui MIPAD V9 2 4 0 KXFCNEK 97354839c6 4 4 zip 这个是小米
  • com.mongodb.MongoSocketReadTimeoutException: Timeout while receiving message

    报错 com mongodb MongoSocketReadTimeoutException Timeout while receiving message at com mongodb connection InternalStreamC
  • C++ list容器详解

    C list容器 list容器的基本概念 1 list的构造函数 2 list的赋值和交换 3 list的大小操作 4 list的插入和删除 5 list的数据存取 6 list的反转与排序 7 list的排序案例 list容器的基本概念
  • Vue 源码解读(12)—— patch

    当学习成为了习惯 知识也就变成了常识 感谢各位的 关注 点赞 收藏和评论 新视频和文章会第一时间在微信公众号发送 欢迎关注 李永宁lyn 文章已收录到 github 仓库 liyongning blog 欢迎 Watch 和 Star 前言
  • linux系统提示只读文件系统,无法创建文件

    可能磁盘写保护 第一步 df h 确定文件夹对应的磁盘 第二步 mount ro为只读 rw为可读可写 可以用mount命令看看ro的分区 如果发现有ro 就重新mount 如 umount dev sda1 mount dev sda1
  • 备战2022,Android中高级面试必知必会

    在过去不久的金九银十 有些小伙伴已经找到了理想的工作 当然也有很多小伙伴因为准备不充分 面试挂了 临近年关 最近有很多网友都在求大厂面试题 正好我在9月份和10月份整理和收集了 Android 中高级面试真题解析 于是就发上来分享给大家 这
  • 如何使用matlab读取excel中的表格数据

    如何使用matlab读取excel中的表格数据 设备系统 win10 操作软件 matlab2020b 1 首先打开matlab软件 点击 新建 脚本 2 在脚本中输入代码 A xlsread C Users Administrator D
  • [附源码]java毕业设计订单管理系统

    项目运行 环境配置 Jdk1 8 Tomcat7 0 Mysql HBuilderX Webstorm也行 Eclispe IntelliJ IDEA Eclispe MyEclispe Sts都支持 项目技术 SSM mybatis Ma
  • 【操作系统】虚拟内存的最大容量和实际容量的区别(以一道例题开头)

    实际内存为什么是2GB 512MB 因为实际容量是取CPU寻址 2 32B 与内存与外存之和 2GB 512MB 的最小值 就是相当于 数学里面两个值取最小值一样
  • gdbserver配置、远程调试以及ssh配置

    引言 GDB调试主要有两种方法 1 直接在目标板上通过gdb调试程序 2 在目标板上通过gdbserver运行程序 在宿主机上通过gdb调试程序 本篇文章主要来说明一下gdbserver远程调试的方法 主要以VScode举例说明 步骤 一
  • idea下载Scala插件(详细)

    目录 1 idea下载Scala 2 点击 Restart IDE 重启IDEA即可 3 创建scala目录 4 Mark scala目录为 source root 5 在windows的电脑安装scala jdk并且配置 环境变量 6 在
  • labelImg支持中文标注的文件

    链接 https pan baidu com s 1XCuLTlKRN7gVxJdQkcKnUw 密码 iaws
  • 读者-写者问题 (操作系统-进程)

    读者 写者问题 读进程优先算法 写者优先算法 问题描述 有读者和写者两组并发进程 共享一个文件 当两个或两个以上的读进程同时访问共享数据时不会产生副作用 但若某个写进程和其他进程 读进程或写进程 同时访问共享数据时则可能导致数据不一致的错误
  • 用vue3+elementplus做的一个滚动菜单栏的组件

    目录 起因 概览 设计及解决思路 1 滚动条竖起来 2 绑定菜单 3 吸附 优化 组件全部代码 起因 在elementplus中看到了滚动条绑定了slider 但是这个感觉很不实用 在底部 而且横向滚动 最常见的应该是那种固定在左上角的带着
  • 交叉编译适配mips架构的GDB

    交叉编译GDB 交叉编译GDB 1 下载GDB源码 2 解压并创建安装目录 3 编译安装 4 可能遇到的错误解决方法 1 下载termcap 2 将上面的编译安装gdb的脚本改一下 3 对于最后的权限不够无法删除PC机上termcap h文
  • 使用UE4(UnrealEngine)创建工程

    UE4系列文章目录 文章目录 UE4系列文章目录 前言 一 步骤 1 打开UE4软件 2 新建工程 3 选择游戏类型模板 4 项目设置 运行游戏 前言 使用UE4 UnrealEngine 创建工程 我这里的ue4版本是4 27 2 一 步
  • stm32循迹小车详细制作过程(附加完全版代码)

    stm32循迹小车详细制作过程 一 材料准备 1 主控板 Stm32f103c8t6 推荐 便宜够用 2 下载器 USB转TTL串口模块 3 电源 12v锂电池组 配套充电器 推荐下图这种 方便 好接线 12v 12v 12v 4 电机驱动
  • No module named ‘dateutil‘解决

    运行程序报错 无法直接pip install dateutil 需要pip install python dateutil
  • [非线性控制理论]6_滑模控制 (sliding mode control)

    非线性控制理论 1 Lyapunov直接方法 非线性控制理论 2 不变性原理 非线性控制理论 3 基础反馈稳定控制器设计 非线性控制理论 4 反馈线性化 反步法 非线性控制理论 5 自适应控制器 Adaptive controller 非线
  • CF 935E - Fafa and Ancient Mathematics

    CF 935E Fafa and Ancient Mathematics 题目描述 定义合法数学表达式 E E E 为一个数或两个合法数学表达式中间加上一个加或减运算符 并且在外面加上一对括号 给定一个合法数学表达式 将其中加减运算符用