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很容易得到. 考虑优化.又有大于的限制又有小于的限制这个非常难处理. 不过可以得到在限制人数上界的情况下能转移到的最远端点 ...
随机推荐
- Flask聚合函数(基本聚合函数、分组聚合函数、去重聚合函数))
Flask聚合函数 1.基本聚合函数(sun/count/max/min/avg) 使用聚合函数先导入:from sqlalchemy import func 使用方法: sun():func.sum ...
- vue 解决axios请求出现前端跨域问题
vue 解决axios请求出现前端跨域问题 最近在写纯前端的vue项目的时候,碰到了axios请求本机的资源的时候,出现了访问报404的问题.这就让我很难受.查询了资料原来是跨域的问题. 在正常开发中 ...
- 【UE4 C++】播放声音、特效
播放声音 PlaySoundAtLocation() USoundCue* HitSound = LoadObject<USoundCue>(this, TEXT("SoundC ...
- 进阶区forgotg攻防世界
攻防世界进阶区--forgot 前言,这题中看不中用啊宝友!!! 1.查看保护 第一反应就是蛮简单的,32位. 2.获取信息(先运行程序看看) 装的可以,蛮多的东西. 但是就是中看不中用 3.ida ...
- Noip模拟29(瞎眼忌) 2021.8.3
T1 最长不下降子序列 在此记录自己的瞎眼... 考场上像一个傻$der$,自己为了防范上升序列和不下降序列的不同特意的造了一组$hack$数据来卡自己:(第一行是序列长度,第二行是序列) 6 1 5 ...
- 查找最小生成树:克鲁斯克尔算法(Kruskal)算法
一.算法介绍 Kruskal算法是一种用来查找最小生成树的算法,由Joseph Kruskal在1956年发表.用来解决同样问题的还有Prim算法和Boruvka算法等.三种算法都是贪心算法的应用.和 ...
- Xpath运算符
5.position定位 >>print tree.xpath('//*[@id="testid"]/ol/li[position()=2]/text()')[0] & ...
- JAVA笔记14__多线程共享数据(同步)/ 线程死锁 / 生产者与消费者应用案例 / 线程池
/** * 多线程共享数据 * 线程同步:多个线程在同一个时间段只能有一个线程执行其指定代码,其他线程要等待此线程完成之后才可以继续执行. * 多线程共享数据的安全问题,使用同步解决. * 线程同步两 ...
- 【Java】String、StringBuffer、StringBuilder
java.lang.String类 概述 String:代表字符串.Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例实现 String声明为final,不可被 ...
- Linux USB (目录)
1.USB 总线简介 2.USB 协议分析 3.USB Host 详解 4.USB Device 详解 5.usbip (USB Over IP) 使用实例