Codeforces 1455G - Forbidden Value(map 启发式合并+DP)
首先这个 if 与 end 配对的结构显然形成一个树形结构,考虑把这棵树建出来,于是这个程序的结构就变为,对树进行一遍 DFS,到达某个节点时,按照顺序遍历其所有儿子,如果儿子是叶子节点则执行赋值操作,否则进入到对应子树中执行相应操作,如果一个节点所有儿子对应的操作都被执行了就回溯,不难发现该过程与原程序等价。
考虑树形 dp。可以发现对于一棵非叶节点,在首次进入这棵子树时,程序中 \(x\) 的值已经确定了——就是 if v 中 \(v\) 的值。因此设 \(dp_{x,j}\) 表示最少花费多少的代价,才能使执行完 \(x\) 子树内的操作后,程序中变量的值为 \(j\),并且程序中变量的值时刻不等于 \(s\)。对于一个子树而言,初始 \(dp_{x,v}=0\),其余 \(dp_{x,i}=\infty\),其中 \(v\) 为进入这棵子树时变量的值,那么我们按顺序遍历这个节点所有子节点 \(y\),分情况讨论:
- 该子节点为赋值操作:那么不妨假设该子节点将变量的值改为 \(p\),删除该操作需要 \(c\) 的代价,那么:
- \(dp_{x,i}\leftarrow dp_{x,i}+c(i\ne p)\)
- \(dp_{x,i}\leftarrow\min\{dp_{x,j}\}(i=p)\)
- 该子节点不是赋值操作:假设这个子树对应的语句为
if v,那么有:- \(dp_{x,i}\leftarrow \min(dp_{x,i},dp_{x,v}+dp_{y,i})(i\ne v)\)
- \(dp_{x,i}\leftarrow dp_{x,v}+dp_{y,i}(i=v)\)
每更新完一步之后都要把 \(dp_{x,s}\) 改为 \(\infty\),因为要保证变量的值时刻不等于 \(s\)。
直接做是平方的。可以发现对于某个 \(x\) 而言,有效的 \(dp_{x,i}\) 的个数是 \(\mathcal O(\text{size}_x)\) 级别的,这不禁让我们想到启发式合并。我们考虑用个 map 维护所有 DP 值。对于一个是赋值操作的儿子 \(y\),我们考虑对其打一个整体加 \(+c\) 的 tag,然后用个 multiset 维护其所有 DP 值,取出 multiset 中最小的元素更新 \(dp_{x,j}\)。对于一个非赋值操作的儿子 \(y\),我们考虑启发式合并如果该节点 \(dp\) 数组的大小 \(>y\) 节点 \(dp\) 数组的大小就暴力用 \(dp_y\) 中的值去更新 \(dp_x\),否则交换 \(dp_x,dp_y\),对 \(dp_x\) 打上一个 \(+dp_{x,v}\) 的标记,然后暴力将现 \(dp_y\) 也就是原 \(dp_x\) 中的元素插入 \(dp_x\) 即可。
时间复杂度 \(n\log^2n\)。细节略有一点点多。
const int MAXN=2e5;
const ll INF=0x3f3f3f3f3f3f3f3fll;
int n,m=1,k,ban,cur=1,val[MAXN+5],fa[MAXN+5];
vector<pii> to[MAXN+5];
int cst[MAXN+5],asv[MAXN+5];
ll tag[MAXN+5];
map<int,ll> dp[MAXN+5];
multiset<ll> st[MAXN+5];
ll get(int x,int v){return (dp[x].count(v))?dp[x][v]:INF;}
void relax(int x){
if(dp[x].count(ban)){
st[x].erase(st[x].find(dp[x][ban]));
dp[x][ban]=INF;st[x].insert(INF);
}
}
void dfs(int x){
// printf("dfs %d\n",x);
dp[x][val[x]]=0;st[x].insert(0);relax(x);
for(pii p:to[x]){
int op=p.fi,id=p.se;
if(op==1){
ll v=min((*st[x].begin())+tag[x],INF);tag[x]+=cst[id];
if(dp[x].count(asv[id])) st[x].erase(st[x].find(dp[x][asv[id]]));
dp[x][asv[id]]=v-tag[x];st[x].insert(v-tag[x]);
} else {
dfs(id);ll nd=get(x,val[id])+tag[x];
if(dp[x].size()>dp[id].size()){
for(pair<int,ll> pp:dp[id]){
int pos=pp.fi;ll v=pp.se+tag[id]+nd,ori=get(x,pos)+tag[x];chkmin(v,INF);
// printf("%d %d %lld\n",id,pp.fi,v);
if(dp[x].count(pos)) st[x].erase(st[x].find(dp[x][pos]));
if(pos==val[id]) dp[x][pos]=v-tag[x];
else dp[x][pos]=min(v,ori)-tag[x];
st[x].insert(dp[x][pos]);
}
} else if(nd<INF) {
// printf("%lld\n",nd);
swap(dp[x],dp[id]);swap(st[x],st[id]);
swap(tag[x],tag[id]);tag[x]+=nd;
for(pair<int,ll> pp:dp[id]){
int pos=pp.fi;ll v=min(pp.se+tag[id],INF);
if(pos!=val[id]){
chkmin(v,get(x,pos)+tag[x]);
if(dp[x].count(pos)) st[x].erase(st[x].find(get(x,pos)));
dp[x][pos]=v-tag[x];st[x].insert(dp[x][pos]);
}
}
}
} relax(x);
// for(pair<int,ll> pp:dp[x]) printf("dp[%d][%d]=%lld\n",x,pp.fi,pp.se+tag[x]);
}
}
int main(){
scanf("%d%d",&n,&ban);val[1]=0;
for(int i=1;i<=n;i++){
static char buf[10];scanf("%s",buf+1);
if(buf[1]=='s'){
int x,v;scanf("%d%d",&x,&v);
cst[++k]=v;asv[k]=x;to[cur].pb(mp(1,k));
} else if(buf[1]=='i'){
int x;scanf("%d",&x);val[++m]=x;
to[cur].pb(mp(2,m));fa[m]=cur;cur=m;
} else cur=fa[cur];
}
// for(int i=1;i<=m;i++) for(pii p:to[i])
// printf("%d -> %d %d\n",i,p.fi,p.se);
dfs(1);printf("%lld\n",(*st[1].begin())+tag[1]);
return 0;
}
Codeforces 1455G - Forbidden Value(map 启发式合并+DP)的更多相关文章
- CDOJ 1284 苦恼的郭大侠 map启发式合并
苦恼的郭大侠 题目连接: http://acm.uestc.edu.cn/#/problem/show/1284 Description 花开雷霆崖,血染伊吕波. 公元1772年. 郭大侠终于照着天行 ...
- CSU 1811: Tree Intersection(线段树启发式合并||map启发式合并)
http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1811 题意:给出一棵树,每一个结点有一个颜色,然后依次删除树边,问每次删除树边之后,分开的两个 ...
- codeforces 375D . Tree and Queries 启发式合并 || dfs序+莫队
题目链接 一个n个节点的树, 每一个节点有一个颜色, 1是根节点. m个询问, 每个询问给出u, k. 输出u的子树中出现次数大于等于k的颜色的数量. 启发式合并, 先将输入读进来, 然后dfs完一个 ...
- 银河战舰 [启发式合并+dp]
题面 思路 我们首先考虑传统的链上LIS做法:保存每个长度的LIS末端的最小值,二分查找 那么这道题其实就只是搬到树上来做了而已 我们考虑一个节点,假设它的儿子已经处理完毕了 那么我们选择LIS最长的 ...
- Codeforces 1166F 并查集 启发式合并
题意:给你一张无向图,无向图中每条边有颜色.有两种操作,一种是询问从x到y是否有双彩虹路,一种是在x到y之间添加一条颜色为z的边.双彩虹路是指:如果给这条路径的点编号,那么第i个点和第i - 1个点相 ...
- Educational Codeforces Round 2 E. Lomsat gelral 启发式合并map
E. Lomsat gelral Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/600/prob ...
- codeforces 741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths(启发式合并)
codeforces 741D Arpa's letter-marked tree and Mehrdad's Dokhtar-kosh paths 题意 给出一棵树,每条边上有一个字符,字符集大小只 ...
- codeforces#1166F. Vicky's Delivery (Service并查集+启发式合并)
题目链接: https://codeforces.com/contest/1166/problem/F 题意: 给出节点数为$n$,边数为$m$的图,保证每个点对都是互连的 定义彩虹路:这条路经过$k ...
- P5979 [PA2014]Druzyny dp 分治 线段树 分类讨论 启发式合并
LINK:Druzyny 这题研究了一下午 终于搞懂了. \(n^2\)的dp很容易得到. 考虑优化.又有大于的限制又有小于的限制这个非常难处理. 不过可以得到在限制人数上界的情况下能转移到的最远端点 ...
随机推荐
- 【Java虚拟机2】Java类加载机制
前言 JAVA代码经过编译从源码变为字节码,字节码可以被JVM解读,使得JVM屏蔽了语言级别的限制.才有了现在的kotlin.Scala.Clojure.Groovy等语言. 字节码文件中描述了类的各 ...
- [对对子队]Scrum Meeting 博客汇总
对对子队 博客目录 一.Scrum Meeting 1. Alpha Scrum Meeting 1(会议记录4.10) Scrum Meeting 2(会议记录4.11) Scrum Meeting ...
- BUAA 2020 软件工程 结对项目作业
Author: 17373051 郭骏 3.28添加:4.计算模块接口的设计与实现过程部分,PairCore实现的细节 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) ...
- OO_JAVA_表达式求导_单元总结
OO_JAVA_表达式求导_单元总结 这里引用个链接,是我写的另一份博客,讲的是设计层面的问题,下面主要是对自己代码的单元总结. 程序分析 (1)基于度量来分析自己的程序结构 第一次作业 程序结构大致 ...
- 并发编程从零开始(八)-ConcurrentHashMap
并发编程从零开始(八)-ConcurrentHashMap 5.5 ConcurrentHashMap HashMap通常的实现方式是"数组+链表",这种方式被称为"拉链 ...
- 【BZOJ 1419】Red is good [概率DP]
我 是 Z Z 概率好玄啊(好吧是我太弱.jpg Description 桌面上有R张红牌和B张黑牌,随机打乱顺序后放在桌面上,开始一张一张地翻牌,翻到红牌得到1美元,黑牌则付出1美元.可以随时停止翻 ...
- openmp学习心得(二)----常见的运行时库函数
omp_set_dynamic();如果设置了动态调整,并行区域会根据系统的资源状况,动态分配线程的数量.好像仅仅有0和非0的区别,设置为0不进行动态分配. omp_get_num_threads,o ...
- ip_local_port_range 和 ip_local_reserved_ports
问题:启动应用程序时,发现网络端口被占用,原因是什么?如何避免? 原因:Linux 系统设置了随机使用的端口范围 echo "40000 60000" > /proc/. ...
- Python爬取COVID-19疫情监控实战
一.项目概述 本项目基于Python.Flask.Echarts打造的一个疫情监控系统,涉及技术: Python网络爬虫 Python与Mysql数据库交互 使用Flask构建web项目 基于Echa ...
- 【做题记录】[NOIP2011 提高组] 观光公交
P1315 [NOIP2011 提高组] 观光公交 我们想在 \(k\) 次加速每一次都取当前最优的方案加速. 考虑怎样计算对于每一条边如果在当前情况下使用加速器能够使答案减少的大小. 如果当前到达某 ...