题解 CF1455G【Forbidden Value】

$\text{Link}$

题意

有 $n$ 行指令,你需要依次执行这些指令。

初始时,你有一个变量 $x=0$,共有三种指令:

  1. set y v,令 $x\gets y$,或用 $v$ 的代价删去这条指令;
  2. if y,如果 $x=y$,则执行其范围内的指令,否则跳过其范围内的指令;
  3. end,用于声明上一个还未声明范围的 if 指令的范围。

给一个数 $s$,求用最少的代价使得没有一个时刻 $x=s$。

$n\le 2\times 10^5$,$s,y\le 2\times 10^5$。

思路

考虑将指令看成树形结构,将每条指令看为直属的 if 指令的子节点。这样子是一个森林,在最外层补上 if 0 end 将其转成树。

考虑树形 dp,先将所有 set 撤销,代价挂在父节点上,令 $dp_{i,j}$ 表示执行 $i$ 子树内的指令,最终 $x=j$ 能撤销的最多代价。

直接做是很不优秀的,我们可以发现 $j$ 的取值只有 $i$ 子树内 $y$ 的取值范围,并且 $i$ 的取值范围是所有子节点取值范围的并,于是可以考虑启发式合并。

对每个结点开 mapmultiset 分别存储 $dp$ 数组和快速修改计算 $dp_{i,k}$ 的最小值。注意在启发式合并将父节点合并到子节点时子节点的 $dp$ 值都未加上其兄弟结点的贡献,开个辅助数值记录全局加 tag 即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff

}
const int N=2e5+10;
int n,s,res,f[N],t[N],v[N],p[N];
ll d[N];//子节点撤销价格之和 
vector<int>a[N];
map<int,ll>dp[N];//可以撤销的贡献 
multiset<ll>mn[N];
inline void dfs(int x){
    mn[x].insert(dp[x][p[x]]=0);
    for(auto to:a[x]){
        if(t[to]==1){
            if(p[to]!=s){
                if(dp[x].count(p[to])){
                    ll tmp=dp[x][p[to]];
                    dp[x][p[to]]=(*mn[x].begin())-v[to];
                    mn[x].erase(mn[x].find(tmp));
                }else{
                    dp[x][p[to]]=(*mn[x].begin())-v[to];
                }
                mn[x].insert(dp[x][p[to]]);
            }
            d[x]+=v[to];
        }else if(dp[x].count(p[to])){
            dfs(to);
            ll tmp=dp[x][p[to]]+d[to];
            if(dp[to].size()<=dp[x].size()){
                for(auto it:dp[to]){
                    int val=it.first;
                    ll cst=it.second;
                    if(!dp[x].count(val)) dp[x][val]=tmp+cst;
                    else{
                        mn[x].erase(mn[x].find(dp[x][val]));
                        if(val==p[to]) dp[x][val]=tmp+cst;
                        else dp[x][val]=min(dp[x][val],tmp+cst);
                    }
                    mn[x].insert(dp[x][val]);
                }
            }else{
                d[x]+=tmp;//全体 +tmp 不好处理 加在 d 上
                for(auto it:dp[x]){
                    int val=it.first;
                    ll cst=it.second;
                    if(!dp[to].count(val)) dp[to][val]=-tmp+cst;
                    else{
                        mn[to].erase(mn[to].find(dp[to][val]));
                        if(val!=p[to]) dp[to][val]=min(dp[to][val],-tmp+cst);
                    }
                    mn[to].insert(dp[to][val]);
                }
                swap(dp[x],dp[to]);
                swap(mn[x],mn[to]);
            }
        }
    }
}
int main(){
    n=read(),s=read();
    for(int i=1;i<=n;i++){
        char opt=getc();
        if(opt=='s'){
            a[res].push_back(i);
            t[i]=1,p[i]=read(),v[i]=read();
        }else if(opt=='i'){
            p[i]=read();
            a[res].push_back(i);
            t[i]=2,f[i]=res,res=i;
        }else{
            res=f[res];
            getc(),getc();
        }
    }
    dfs(0);
    write(d[0]+(*mn[0].begin()));
    flush();
}

再见 qwq~

posted @ 2022-07-13 09:43  ffffyc  阅读(10)  评论(0)    收藏  举报  来源