1455G Forbidden Value(数据结构优化dp+启发式合并)
Educational Codeforces Round 99 (Rated for Div. 2)
G. Forbidden Value
题意:给一个初始值为 0 0 0 的变量 x x x 以及一段只包含 s e t 、 i f 、 e n d set、if、end set、if、end 的简易代码段,代码 “ 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 1≤n≤2e5,1≤s≤2e5,0≤y≤2e5,1≤v≤1e9。
分析: 可以先考虑基本的 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】感谢观看