CF1799 D. Hot Start Up (easyhard version) [dp+不停的优化+线段树]

传送门:CF

[前题提要]:D1思维难度不高;D2感觉十分变态,感觉就是为了出题而出题,但是竟然只有*2100,看来还是我太菜了…


E a s y v e r s i o n : Easy\;version: Easyversion:

不难想到应该使用 d p dp dp来解决这道题.仔细模拟一下,就会得到一个朴素的定义:考虑定义 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]为加入了前 i i i个任务,第1个CPU最后完成的任务是第 j j j个任务,第2个CPU最后完成的任务是第 k k k个任务的最小花费.发现第一维显然可以使用滚动数组滚掉,考虑重定义为 d p [ 0 / 1 ] [ j ] [ k ] dp[0/1][j][k] dp[0/1][j][k].但是此时的复杂度依旧是 n 2 k n^2k n2k.

仔细观察一下,会发现其实是存在这样一个性质的,我们会发现对于加入的第 i i i个任务,我们必然存在一个CPU最后一个任务是 i i i.所以此时我们就可以不用一起枚举两个CPU的状态,我们可以假定任意一个CPU放第 i i i个任务,这样我们就可以将复杂度降为 n 2 n^2 n2或者 n k nk nk了.

具体来说,我们有以下递推方程:
d p [ n o w ] [ i ] [ j ] = d p [ p r e ] [ i − 1 ] [ j ] + h o t o r c o l d dp[now][i][j]=dp[pre][i-1][j]+hot\;or\;cold dp[now][i][j]=dp[pre][i1][j]+hotorcold 将第 i i i个任务放在最后状态为 i − 1 i-1 i1的CPU1上
d p [ n o w ] [ i − 1 ] [ i ] = d p [ p r e ] [ i − 1 ] [ j ] + h o t o r c o l d , j ∈ [ 0 , i − 2 ] dp[now][i-1][i]=dp[pre][i-1][j]+hot\;or\;cold,j\in[0,i-2] dp[now][i1][i]=dp[pre][i1][j]+hotorcold,j[0,i2] 将第 i i i个任务放在最后状态为 j j j的CPU2上
d p [ n o w ] [ j ] [ i ] = d p [ p r e ] [ j ] [ i − 1 ] + h o t o r c o l d dp[now][j][i]=dp[pre][j][i-1]+hot\;or\;cold dp[now][j][i]=dp[pre][j][i1]+hotorcold 将第 i i i个任务放在最后状态为 i − 1 i-1 i1的CPU2上
d p [ n o w ] [ i ] [ i − 1 ] = d p [ p r e ] [ j ] [ i − 1 ] + h o t o r c o l d , j ∈ [ 0 , i − 2 ] dp[now][i][i-1]=dp[pre][j][i-1]+hot\;or\;cold,j\in[0,i-2] dp[now][i][i1]=dp[pre][j][i1]+hotorcold,j[0,i2] 将第 i i i个任务放在最后状态为 j j j的CPU1上
此时我们就可以解决 E a s y v e r s i o n Easy\;version Easyversion了,具体代码放在文章末尾.


H a r d v e r s i o n : Hard\;version: Hardversion:

发现范围变大了.对于这种dp题来说,范围变大了,必然是需要某种数据结构进行优化.

但是我们 E a s y Easy Easy版本的 d p dp dp方程太 n a i v e naive naive了,甚至没有优化的资格.

考虑优化一下我们的上面的dp方程,我们会发现其实四种状态和之前的状态都是没有交集的,所以我们其实可以将第一维直接优化掉.根本不需要进行滚动.但是此时我们会发现依旧很难向数据结构那边靠.
所以我们还需要进行优化.继续观察dp方程,我们会发现 1 , 3 1,3 1,3以及 2 , 4 2,4 2,4的状态似乎是对称的.进一步,我们会发现其实上述两种状态是可以进行合并的.考虑优化我们的dp方程的定义,重定义 d p [ i ] [ j ] dp[i][j] dp[i][j]为当前枚举到了第 i i i个任务,其中一个CPU的状态以 i i i为下标的任务,另外一个CPU最后的状态以 j j j为下标的任务最小贡献(因为其中一个CPU最后状态必然是 i i i).那么此时我们的递推方程就变成了下面这个:

d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + h o t o r c o l d dp[i][j]=dp[i-1][j]+hot\;or\;cold dp[i][j]=dp[i1][j]+hotorcold 将第 i i i个任务放在最后状态为 i − 1 i-1 i1的CPU上
d p [ i ] [ i − 1 ] = m i n { d p [ i − 1 ] [ j ] + h o t o r c o l d } dp[i][i-1]=min\{dp[i-1][j]+hot\;or\;cold\} dp[i][i1]=min{dp[i1][j]+hotorcold} 将第 i i i个任务放在最后状态为 j j j的CPU上

此时我们就可以看出一些端倪了.我们会发现上述dp仍然可以进行滚动.我们借这个滚动来看一下这个性质,我们会发现第一个dp方程就是在原来所有状态的基础上进行一个区间加(因为滚动掉之后左右下标不变),第二个dp方程就是在原本所有状态的基础上进行一个区间加然后再取一个min,将其赋给i-1状态.此时还需要注意的是,第一个dp方程和第二个dp方程之间是有交集的,所以我们得同时修改两个dp方程,不然会导致状态紊乱.此时有经验的人应该想到使用线段树进行维护了.但是仍然存在一个问题,按照上述的状态,我们很难判断 j j j任务和 i i i任务是否相同.此时也就很难使用线段树进行维护了,因为对于不同的 j j j我们既需要加 h o t hot hot又要加 c o l d cold cold.此时我们继续优化 d p dp dp方程,我们可以将 d p dp dp继续重定义为为当前枚举到了第 i i i个任务,其中一个CPU的状态为以 i i i为下标的任务,另外一个CPU最后的状态第 j j j种任务的最小贡献.此时我们就可以使用线段树来进行维护了.因为只有状态为 a [ i ] a[i] a[i]的那个节点才需要加 h o t hot hot,其他的都加 c o l d cold cold即可,我们大可以将区间分成三段来分别考虑.具体维护方式见代码.


下面是具体的代码部分( E a s y Easy Easy版本):

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls (rt<<1)
#define rs (rt<<1|1)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {ll x=0,w=1;char ch=getchar();for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';return x*w;
}
inline void print(__int128 x){if(x<0) {putchar('-');x=-x;}if(x>9) print(x/10);putchar(x%10+'0');
}
#define maxn 1000000
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int a[maxn];
struct Node{int cold,hot;
}node[maxn];ll dp[2][5010][5010];
int main() {int T=read();while(T--) {int n=read();int k=read();for(int i=1;i<=n;i++) a[i]=read();for(int i=1;i<=k;i++) node[i].cold=read();for(int i=1;i<=k;i++) node[i].hot=read();for(int i=0;i<=n;i++) {for(int j=0;j<=n;j++) {dp[0][i][j]=dp[1][i][j]=ll_INF;}}int pre=0,now=1;dp[0][0][0]=0;for(int i=1;i<=n;i++) {//i-1放上面for(int j=0;j<=n;j++) {//i放上面if(a[i-1]==a[i]) dp[now][i][j]=min(dp[now][i][j],dp[pre][i-1][j]+node[a[i]].hot);else dp[now][i][j]=min(dp[now][i][j],dp[pre][i-1][j]+node[a[i]].cold);//i放下面if(a[j]==a[i]) dp[now][i-1][i]=min(dp[now][i-1][i],dp[pre][i-1][j]+node[a[i]].hot);else dp[now][i-1][i]=min(dp[now][i-1][i],dp[pre][i-1][j]+node[a[i]].cold);}//i-1放下面for(int j=0;j<=n;j++) {//i放上面if(a[j]==a[i]) dp[now][i][i-1]=min(dp[now][i][i-1],dp[pre][j][i-1]+node[a[i]].hot);else dp[now][i][i-1]=min(dp[now][i][i-1],dp[pre][j][i-1]+node[a[i]].cold);//i放下面if(a[i-1]==a[i]) dp[now][j][i]=min(dp[now][j][i],dp[pre][j][i-1]+node[a[i]].hot);else dp[now][j][i]=min(dp[now][j][i],dp[pre][j][i-1]+node[a[i]].cold);}if(i!=n) {for(int j=0;j<=n;j++) {dp[pre][j][i-1]=ll_INF;dp[pre][i-1][j]=ll_INF;}}swap(pre,now);}ll ans=ll_INF;for(int i=0;i<=n;i++) {for(int j=0;j<=n;j++) {ans=min(ans,dp[pre][i][j]);}}cout<<ans<<endl;}return 0;
}

下面是具体的代码部分(Hard版本):
PS:存在0所以将所有下标都右移一位

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls (rt<<1)
#define rs (rt<<1|1)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {ll x=0,w=1;char ch=getchar();for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';return x*w;
}
inline void print(__int128 x){if(x<0) {putchar('-');x=-x;}if(x>9) print(x/10);putchar(x%10+'0');
}
#define maxn 1000000
#define int long long
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int a[maxn];
struct Node{int cold,hot;
}node[maxn];
struct Segment_tree{int l,r,lazy,mn;
}tree[maxn*4];
void pushup(int rt) {tree[rt].mn=min(tree[ls].mn,tree[rs].mn);
}
void build(int l,int r,int rt) {tree[rt].l=l;tree[rt].r=r;tree[rt].lazy=0;tree[rt].mn=ll_INF;if(l==r) {if(l==1) tree[rt].mn=0;return ;}int mid=(l+r)>>1;build(lson);build(rson);pushup(rt);
}
void change(int rt,int val) {tree[rt].mn+=val;
}
void pushdown(int rt) {change(ls,tree[rt].lazy);change(rs,tree[rt].lazy);tree[rt].lazy=0;
}
void update(int l,int r,int rt,int val) {if(tree[rt].l==l&&tree[rt].r==r) {change(rt,val);return ;}if(tree[rt].lazy) pushdown(rt);int mid=(tree[rt].l+tree[rt].r)>>1;if(r<=mid) update(l,r,ls,val);else if(l>mid) update(l,r,rs,val);else update(l,mid,ls,val),update(mid+1,r,rs,val);pushup(rt);
}
int query(int l,int r,int rt) {if(tree[rt].l==l&&tree[rt].r==r) {return tree[rt].mn;}if(tree[rt].lazy) pushdown(rt);int mid=(tree[rt].l+tree[rt].r)>>1;if(r<=mid) return query(l,r,ls);else if(l>mid) return query(l,r,rs);else return min(query(l,mid,ls),query(mid+1,r,rs));
}
signed main() {int T=read();while(T--) {int n=read();int k=read();for(int i=1;i<=n;i++) {a[i]=read();}for(int i=1;i<=k;i++) {node[i].cold=read();}for(int i=1;i<=k;i++) {node[i].hot=read();}build(1,k+1,1);for(int i=1;i<=n;i++) {update(1,a[i],1,node[a[i]].cold);if(a[i]+2<=k+1)update(a[i]+2,k+1,1,node[a[i]].cold);update(a[i]+1,a[i]+1,1,node[a[i]].hot);int num=query(1,k+1,1);//恢复原状,避免状态紊乱update(1,a[i],1,-node[a[i]].cold);if(a[i]+2<=k+1)update(a[i]+2,k+1,1,-node[a[i]].cold);update(a[i]+1,a[i]+1,1,-node[a[i]].hot);if(a[i-1]==a[i]) {update(1,k+1,1,node[a[i]].hot);}else {update(1,k+1,1,node[a[i]].cold);}int num2=query(a[i-1]+1,a[i-1]+1,1);//取一个最大值if(num<num2) {update(a[i-1]+1,a[i-1]+1,1,-(num2-num));}}cout<<query(1,k+1,1)<<endl;}return 0;
}

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

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

相关文章

国家开放大学电大《国际私法》形考任务答案

答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 1969年《国际油污损害民事责任公约》实行的是&#xff08; &…

金融投贷通(金融投资+贷款通)项目准备

金融投贷通&#xff08;金融投资贷款通&#xff09;项目准备 专业术语投资专业术语本息专业术语还款专业术语项目介绍三个子系统技术架构核心流程发布借款标投资业务 项目实施测试流程测试步骤 专业术语 投资专业术语 案例&#xff1a;张三借给李四5W&#xff0c;约定期满1年后…

NPL实例

自然语言处理&#xff08;NLP&#xff09;是人工智能领域的一个重要技术分支&#xff0c;它旨在使计算机能够理解和处理人类语言。以下是一些常见的NLP技术的例子&#xff1a; 1. 机器翻译&#xff1a;NLP技术可以用于将一种语言翻译成另一种语言。例如&#xff0c;Google翻译…

【深度学习】基础知识

吴恩达DeepLearning Python # 1.numpy c c.ravel() 将多维数组拉平 # 2.time tic time.time() toc time.time() print(str(1000*(toc- tic))"ms")

畅捷通T+ Ufida.T.DI.UIP.RRA.RRATableController 远程命令执行漏洞

一、漏洞信息 漏洞名称&#xff1a;畅捷通T Ufida.T.DI.UIP.RRA.RRATableController 远程命令执行漏洞 漏洞类别&#xff1a;远程命令执行漏洞 风险等级&#xff1a;高危 二、漏洞描述 畅捷通TPlus适用于异地多组织、多机构对企业财务汇总的管理需求&#xff1b;全面支持企…

AI论文速读 |(Mamba×时空图预测!) STG-Mamba:通过选择性状态空间模型进行时空图学习

&#xff08;来了来了&#xff0c;虽迟但到&#xff0c;序列建模的新宠儿mamba终于杀入了时空预测&#xff01;&#xff09; 论文标题&#xff1a;STG-Mamba: Spatial-Temporal Graph Learning via Selective State Space Model 作者&#xff1a;Lincan Li, Hanchen Wang&…

C语言经典例题(10) --- 奇偶统计、密码验证、矩阵计算、最低分与最高分之差、序列中删除指定数字

文章目录 1.奇偶统计2.密码验证3.矩阵计算4.最低分与最高分之差5.序列中删除指定数字 1.奇偶统计 题目描述: 任意输入一个正整数N&#xff0c;统计1~N之间奇数的个数和偶数的个数&#xff0c;并输出。 输入描述: 一行&#xff0c;一个正整数N。&#xff08;1≤N≤100,000&am…

​网络安全概论——网络加密与密钥管理​

一、网络加密的方式及实现 1、常见的加密算法 常见的密钥加密算法类型大体可以分为三类:对称加密、非对称加密、单向加密。 对称加密算法采用单密钥加密&#xff0c;在通信过程中&#xff0c;数据发送方将原始数据分割成固定大小的块&#xff0c;经过密钥和加密算法逐个加密…

LLM应用:Prompt flow vs LangChain

背景 Prompt flow和LangChain都是LLM时代&#xff0c;为高效地构建LLM应用而生。 Prompt flow是Microsoft开源的&#xff0c;其诞生时&#xff0c;LangChain已经很有名气了。 所以作为后生的Prompt flow会为我们带来哪些新的东西呢&#xff1f; ​​​​​​​ Prompt flo…

Solana 低至 0.4 Sol 创建OpenBook市场ID教程

Raydium上线代币之前&#xff0c;需要OpenBook ID&#xff0c;但是Raydium官方提供的链接创建需要花费 3-4 SOL。这成本使得我们对发行代币望而却步。 本篇文章介绍OpenBook的概念和教大家如何更低成本 (最低0.4 SOL) 创建 OpenBook Market ID。 目录 1、Raydium加池子创建为什…

实名羡慕!这些人已经用上了Sora

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了人工智能中文站https://ai.weoknow.com 每天给大家更新可用的国内可用chatGPT资源 发布在https://it.weoknow.com 更多资源欢迎关注 Sora 第三方口碑出炉&#xff0c;或许带给艺术家们最大的好处是&#xff1a;…

第1章递归函数

第1章 递归函数的设计技巧 数学归纳法 递归函数设计三个重要部分 递归求阶乘 数学(结构)归纳法 验证P(1)成立证明如果P(k)成立&#xff0c;那么P(k1)成立联合Step1和Step2,证明P(1)->P(n)成立 递归函数 给递归函数一个明确的语义实现边界条件时的程序逻辑&#xff0…

C++入门 (1) >>命名空间与缺省参数

1. c与c语言的区别 c兼容c语言90&#xff05;以上的语法与规则&#xff0c;c语言相当于用锤子和凿子制作工艺品&#xff0c;c相当于用电钻&#xff0c;电动雕刻刀制作工艺品。 2. c的框架 #include<iostream> //stdio.h的升级版 using namespace std; //展开命…

项目搭建之统一返回值

自定义枚举类 Getter public enum ReturnCodeEnum {/*** 操作失败**/RC999("999","操作XXX失败"),/*** 操作成功**/RC200("200","success"),/*** 服务降级**/RC201("201","服务开启降级保护,请稍后再试!"),/*** …

【笔记】Python学习记录

Python学习记录 Hello World变量简单数据类型字符串大小写转换插入变量Tab和Enter删除前后空格删除前后缀 Hello World 老调调了&#xff0c;如何在终端输出信息呢&#xff1f; print("Hello World")Hello World变量 变量命名遵从代码变量命名通则&#xff0c;几乎…

3.27作业

1、完成下面类 #include <iostream> #include <cstring> using namespace std;class myString { private:char *str; //记录c风格的字符串int size; //记录字符串的实际长度 public://无参构造myString():size(10){str new char[size]; …

php魔术方法

PHP 中的魔术方法&#xff08;Magic Methods&#xff09;是一组具有特殊名称的方法&#xff0c;它们会在特定的事件发生时自动被调用。这些事件包括对象的创建、销毁、属性的访问和修改等。通过使用魔术方法&#xff0c;你可以更好地控制对象的行为&#xff0c;并增加代码的灵活…

计算机网络安全 —— 非对称加密算法 RSA 和数字签名

一、非对称加密算法基本概念 ​ 在对称密钥系统中&#xff0c;两个参与者要共享同一个秘密密钥。但怎样才能做到这一点呢&#xff1f;一种是事先约定&#xff0c;另一种是用信使来传送。在高度自动化的大型计算机网络中&#xff0c;用信使来传送密钥显然是不合适的。如果事先约…

【动态规划】【卡特兰数】Leetcode 96. 不同的二叉搜索树

【动态规划】【卡特兰数】Leetcode 96. 不同的二叉搜索树 动态规划卡特兰数 ---------------&#x1f388;&#x1f388;96. 不同的二叉搜索树 题目链接&#x1f388;&#x1f388;------------------- 动态规划 &#x1f612;: 我的代码实现> 动规五部曲 ✒️确定dp数组…

Impala中操作Kudu表的语法

文章目录 前言一、相关介绍1. 内部表和外部表1.1 内部表1.2 外部表 2. 分区表 二、Impala中操作Kudu表的语法1. 创建Kudu外部表2. 创建Kudu内部表2.1 CTAS&#xff08;CREATE TABLE AS SELECT&#xff09;2.2 创建范围分区表2.3 创建哈希分区表2.4 同时使用范围分区和哈希分区2…