acm-(好题、STL、dp、模拟)Educational Codeforces Round 99 (Rated for Div. 2) G. Forbidden Value

27 篇文章 0 订阅
11 篇文章 1 订阅

题面
传送门
考虑设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示读完第 i i i行语句后,若 x x x值为 j j j所需要花费的最小代价。

假设本题只有 s e t set set语句,那么我们不难发现,每当遇到语句 s e t    y    v set\;y\;v setyv时我们只需要更新 ∀ j ≠ y , d p [ i ] [ j ] + = v \forall j\ne y,dp[i][j]+=v j=y,dp[i][j]+=v以及 d p [ i ] [ y ] = m i n k { d p [ i − 1 ] [ k ] } dp[i][y]=min_{k}\{dp[i-1][k]\} dp[i][y]=mink{dp[i1][k]},也就是实现一个全局加和单点修改的功能即可,至于求最小值我们考虑用 m u l t i s e t multiset multiset来维护。
具体来说,我们用懒标记 a d ad ad表征全局的增益,一个 m a p map map数组 d p [ j ] dp[j] dp[j]来记录第 i i i条语句对应的 d p dp dp值,将这些值放入 m u l t i s e t multiset multiset中,更新的时候取 m u l t i s e t multiset multiset中的最小值更新 d p [ y ] dp[y] dp[y]即可。

本题稍微复杂的就是加入了 i f if if语句,进入 i f    y if\;y ify语句显然只能从 d p [ y ] dp[y] dp[y]的这个状态转移过去,也就是说一旦进入 i f if if语句,那么最基础的成本就是 d p [ y ] dp[y] dp[y]了,并且起始数字一定是 x = y x=y x=y,注意到整个游戏开始的时候,我们的 x = 0 x=0 x=0,对应的基础成本是 0 0 0,起始本质上 i f if if语句内的情况是一样的,只是初始条件不同,因此我们一旦进入 i f if if中(如果可以进入)就新开一个 d p dp dp数组,单独更新,对应的初始条件是 d p [ y ] dp[y] dp[y],更新完毕后,若退出了 i f if if块,那么我们就这个新的 d p dp dp数组与外面的 d p dp dp数组合并,也就是说对所有的 d p [ j ] dp[j] dp[j]可能存在两个值,那么取最小的那个即可。如果还有 i f if if的嵌套的话,我们继续开新的 d p dp dp数组,具体来说,这么多的 d p dp dp数组存在父子关系,我们用栈维护即可,退出 i f if if块的时候弹栈并将两个 d p dp dp数组合并,进入 i f if if块的时候 p u s h push push新的 d p dp dp数组。

struct Node{
	multiset<ll>s;
	unordered_map<int,ll>dp;
	ll ad=0;
};


vector<Node>g;
void upd(Node &a,int x,ll v){
    if(a.dp.count(x)){
        a.s.erase(a.s.find(a.dp[x]));
    }
    a.dp[x]=v;
    a.s.insert(v);
}
int gnew(int x,ll v){
	g.push_back({});
	g.back().dp[x]=v;
	g.back().s.insert(v);
	g.back().ad=0;
	return (int)g.size()-1;
}
char ch[10];
int main(){
	int t=rd(),s=rd();
	int u=gnew(0,0),skip=0;
	while(t--){
		int y,v;
		rds(ch);
		if(ch[0]=='s'){
			y=rd(),v=rd();
			if(skip)continue;
            ll mi=*g[u].s.begin()-v;
			g[u].ad+=v;
			if(y!=s)upd(g[u],y,mi);
		}else if(ch[0]=='i'){
			y=rd();
			if(skip || !g[u].dp.count(y)){
				skip++;
				continue;
			}
			int vv=gnew(y,g[u].dp[y]+g[u].ad);
            g[u].s.erase(g[u].s.find(g[u].dp[y]));
            g[u].dp.erase(y);
			u=vv;
		}else{
			if(skip)skip--;
			else {
                unordered_map<int,ll>::iterator it1,it2;
                if(g[u].dp.size()>g[u-1].dp.size())swap(g[u],g[u-1]);
                for(it1=g[u].dp.begin(),it2=g[u].dp.end();it1!=it2;it1++){
                    if(!g[u-1].dp.count(it1->first) || g[u-1].dp[it1->first]+g[u-1].ad>it1->second+g[u].ad){
                        upd(g[u-1],it1->first,it1->second+g[u].ad-g[u-1].ad);
                    }
                }
				g.pop_back();
				u--;
			}
		}
	}
	wrn(*g[u].s.begin()+g[u].ad);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值