1455G Forbidden Value(数据结构优化dp+启发式合并)

1455G Forbidden Value(数据结构优化dp+启发式合并)

Educational Codeforces Round 99 (Rated for Div. 2)

G. Forbidden Value

题面Forbidden Value

题意:给一个初始值为 0 0 0 的变量 x x x 以及一段只包含 s e t 、 i f 、 e n d set、if、end setifend 的简易代码段,代码 “ s e t   y   v set~y~v set y v” 表示将 x x x 赋值为 y y y,或者花费 v v v 的代价不执行该语句,代码块 i f   y   . . .   e n d if~y~...~end if y ... end 为条件语句块,当 x x x 的值等于 y y y 时才会进入该代码块,代码块可以嵌套。在程序的运行过程当中一旦 x x x 的值变成了 s s s 就会立马崩溃,现在问至少要花费多少的代价才能让程序正常跑完。

范围 1 ≤ n ≤ 2 e 5 , 1 ≤ s ≤ 2 e 5 , 0 ≤ y ≤ 2 e 5 , 1 ≤ v ≤ 1 e 9 1 \le n \le 2e5, 1 \le s \le 2e5, 0 \le y \le 2e5, 1 \le v \le 1e9 1n2e5,1s2e5,0y2e5,1v1e9

分析: 可以先考虑基本的 d p dp dp d p i j dp_{ij} dpij 表示执行完第 i i i 条语句后 x x x 的值为 j j j 所需要的最小代价。若代码段中只存在 s e t set set 语句的话这个 d p dp dp 很好写,只需要注意进行离散化。现在考虑 i f   . . .   e n d if~...~end if ... end 语句块,可以发现处理语句块内部只是与本道题本质相同规律减少的子问题,因此我们可以对该问题进行递归的求解,假设进入内部 i f   . . .   e n d if~...~end if ... end 语句块所需要的代价为 c o s t cost cost,那么在处理完内部 i n n e r _ d p inner\_dp inner_dp 后与外部 o u t e r _ d p outer\_dp outer_dp 进行合并时 o u t e r _ d p i j = m i n ( o u t e r _ d p i j , c o s t + i n n e r _ d p i j ) outer\_dp_{ij} = min(outer\_dp_{ij}, cost + inner\_dp_{ij}) outer_dpij=min(outer_dpij,cost+inner_dpij) 即可,总体的时间复杂度为 O ( n 2 ) O(n^2) O(n2),考虑进行优化。

两个大方向可以进行优化:

① 对于一条 s e t   y   v set~y~v set y v 语句,除了 j = = y j == y j==y 其余所有的 j j j 为了保持原来的值都需要增加 v v v 的代价,而 j = = y j == y j==y 在这条语句过后一定会保持 j = = y j == y j==y,因此在这条语句之前 j j j 可以是任何值,所以我们应当取其中代价最小的。注意到每次只有一个 j j j 的变化不同,其他 j j j 的变化都相同,因此我们可以设置一个偏移量 o f f s e t offset offset 保存所有 j j j 的变化量,只修改一个 j = = y j == y j==y 的值,这里我们需要一个快速单点修改以及求全局最小值的功能,STL 中的容器 S e t Set Set 可以满足,时间复杂度从 O ( n ) O(n) O(n) 降低为 O ( l o g n ) O(logn) O(logn)

② 上述的朴素 d p dp dp 合并方式的时间复杂度为 O ( n 2 ) O(n^2) O(n2),这里可以使用启发式合并的方式,每次合并的时候都是 s i z e size size 较小的 d p dp dp 合并至 s i z e size size 较大的 d p dp dp,时间复杂度从 O ( n 2 ) O(n^2) O(n2) 降低为 O ( n l o g n ) O(nlogn) O(nlogn),不过这里我们需要维护 S e t Set Set 等数据结构,时间复杂度为 O ( n l o g n l o g n ) O(nlognlogn) O(nlognlogn)

通过上述的优化,数据结构 s e t set set m a p map map 优化的 d p dp dp 配合启发式合并,将时间复杂度减小到 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n),足以通过本题。

Code

#include <bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
 
inline int read()
{
    int s = 0, w = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        s = s * 10 + ch - '0', ch = getchar();
    return s * w;
}
 
const int MAXN = 2e5 + 10;
const int INF = 1e18;
const int MOD = 1e9 + 7;
const double eps = 1e-9;
const double PI = acos(-1.0);
 
int n, s;
 
struct Node
{
	int offset;
	map<int, int> mp;
	multiset<int> ms;
};
 
vector<Node> vec;
 
void update(Node &node, int y, int v)
{
	if (node.mp.count(y))
	{
		node.ms.erase(node.ms.find(node.mp[y]));
	}
	node.mp[y] = v;
	node.ms.insert(v);
}
 
signed main()
{
    n = read(), s = read();
    int skip = 0;
    vec.push_back({});
    vec.back().offset = 0;
    vec.back().mp[0] = 0;
    vec.back().ms.insert(0);
    for (int i = 0; i < n; i++)
    {
    	string op;
    	int y, v;
    	cin >> op;
    	if (op[0] == 's')
    	{
    		y = read(), v = read();
    		if (skip) continue;
    		int minV = *vec.back().ms.begin() - v;
    		vec.back().offset += v;
    		if (y != s)
    		{
    			update(vec.back(), y, minV);
    		}
    	}
    	else if (op[0] == 'i')
    	{
    		y = read();
    		if (!skip && vec.back().mp.count(y))
    		{
    			int val = vec.back().mp[y] + vec.back().offset;
    			vec.back().ms.erase(vec.back().ms.find(vec.back().mp[y]));
    			vec.back().mp.erase(y);
    			vec.push_back({});
    			vec.back().offset = 0;
    			update(vec.back(), y, val);
    		}
    		else
    		{
    			skip++;
    		}
    	}
    	else
    	{
    		if (skip) skip--;
    		else
    		{
    			if (vec[vec.size() - 1].mp.size() > vec[vec.size() - 2].mp.size())
    			{
    				swap(vec[vec.size() - 1], vec[vec.size() - 2]);
    			}
    			auto &outer = vec[vec.size() - 2];
    			for (auto x : vec.back().mp)
    			{
    				if (!outer.mp.count(x.first) || (outer.offset + outer.mp[x.first] > vec.back().offset + x.second))
    				{
    					int val = vec.back().offset + x.second - outer.offset;
    					update(outer, x.first, val);
    				}
    			}
    			vec.pop_back();
    		}
    	}
    }
    cout << vec.back().offset + *vec.back().ms.begin() << endl;
    return 0;
}

【END】感谢观看

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值