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;
}

浙公网安备 33010602011771号