Codeforces 题面传送门 & 洛谷题面传送门

首先这个 ifend 配对的结构显然形成一个树形结构,考虑把这棵树建出来,于是这个程序的结构就变为,对树进行一遍 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)的更多相关文章

  1. CDOJ 1284 苦恼的郭大侠 map启发式合并

    苦恼的郭大侠 题目连接: http://acm.uestc.edu.cn/#/problem/show/1284 Description 花开雷霆崖,血染伊吕波. 公元1772年. 郭大侠终于照着天行 ...

  2. CSU 1811: Tree Intersection(线段树启发式合并||map启发式合并)

    http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1811 题意:给出一棵树,每一个结点有一个颜色,然后依次删除树边,问每次删除树边之后,分开的两个 ...

  3. codeforces 375D . Tree and Queries 启发式合并 || dfs序+莫队

    题目链接 一个n个节点的树, 每一个节点有一个颜色, 1是根节点. m个询问, 每个询问给出u, k. 输出u的子树中出现次数大于等于k的颜色的数量. 启发式合并, 先将输入读进来, 然后dfs完一个 ...

  4. 银河战舰 [启发式合并+dp]

    题面 思路 我们首先考虑传统的链上LIS做法:保存每个长度的LIS末端的最小值,二分查找 那么这道题其实就只是搬到树上来做了而已 我们考虑一个节点,假设它的儿子已经处理完毕了 那么我们选择LIS最长的 ...

  5. Codeforces 1166F 并查集 启发式合并

    题意:给你一张无向图,无向图中每条边有颜色.有两种操作,一种是询问从x到y是否有双彩虹路,一种是在x到y之间添加一条颜色为z的边.双彩虹路是指:如果给这条路径的点编号,那么第i个点和第i - 1个点相 ...

  6. 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 ...

  7. 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 题意 给出一棵树,每条边上有一个字符,字符集大小只 ...

  8. codeforces#1166F. Vicky's Delivery (Service并查集+启发式合并)

    题目链接: https://codeforces.com/contest/1166/problem/F 题意: 给出节点数为$n$,边数为$m$的图,保证每个点对都是互连的 定义彩虹路:这条路经过$k ...

  9. P5979 [PA2014]Druzyny dp 分治 线段树 分类讨论 启发式合并

    LINK:Druzyny 这题研究了一下午 终于搞懂了. \(n^2\)的dp很容易得到. 考虑优化.又有大于的限制又有小于的限制这个非常难处理. 不过可以得到在限制人数上界的情况下能转移到的最远端点 ...

随机推荐

  1. 微信小程序中路由跳转

    一.是什么 微信小程序拥有web网页和Application共同的特征,我们的页面都不是孤立存在的,而是通过和其他页面进行交互,来共同完成系统的功能 在微信小程序中,每个页面可以看成是一个pageMo ...

  2. TypeError: module() takes at most 2 arguments (3 given)

    1. 错误提示 2. 代码 class Parent: """定义父类""" def __init__(self): print(" ...

  3. Redis 高阶数据类型重温

    今天这个专题接着上一篇 Redis 的基本数据类型 继续讲解剩下的高阶数据类型:BitMap.HyperLogLog 和 GEO hash.这些数据结构的底层也都是基于我们前面说的 5 种 基本类型, ...

  4. Shiro反序列化的检测与利用

    1. 前言 Shiro 是 Apache 旗下的一个用于权限管理的开源框架,提供开箱即用的身份验证.授权.密码套件和会话管理等功能. 2. 环境搭建 环境搭建vulhub 3. 如何发现 第一种情况 ...

  5. [对对子队]Beta阶段项目展示博客

    Beta阶段项目展示博客 1 团队成员的简介和个人博客地址 成员 头像 岗位 博客 个人介绍 黄贤昊 PM 17373253 喜欢玩游戏和做游戏,项目经验基本都和游戏相关,擅长摸鱼,偶尔敬业. 吴桐雨 ...

  6. Spring Cloud Alibaba Nacos Config 的使用

    Spring Cloud Alibaba Nacos Config 的使用 一.需求 二.实现功能 1.加载 product-provider-dev.yaml 配置文件 2.实现配置的自动刷新 3. ...

  7. Linux C 数据结构 ->单向链表

    之前看到一篇单向链表的博文,代码也看着很舒服,于是乎记录下来,留给自己~,循序渐进,慢慢 延伸到真正的内核链表~(敢问路在何方?路在脚下~) 1. 简介 链表是Linux 内核中最简单,最普通的数据结 ...

  8. readelf

    readelf的help内容如下所示: Usage: readelf <option(s)> elf-file(s) Display information about the conte ...

  9. poj 3020 Antenna Placement(二分图最大匹配)

    题意: N行M列的矩阵,每个格子里不是 * 就是 O . * :是一个利益点. O:是一个空白点. 每次可以用一个圈覆盖相邻的两个*.(左右相邻或上下相邻). 问最少需要多少个圈可以覆盖所有的*. 思 ...

  10. Spring Cloud Alibaba 使用RestTemplate进行服务消费

    创建服务提供者工程 创建spring-cloud-alibaba-service-member工程,会员中心服务该服务提供用户会员信息. pom.xml <?xml version=" ...