CF1455G

Statement

题目写的很清楚了,看题目的翻译吧。

Solution

考虑 if 指令形成了一个嵌套关系,是一个层层包含的过程。

于是可以将每个 if 指令与被他包含的指令之间连边,然后在开头加上一个指令 if 0,这样,我们就将题目所示的关系建成了一颗树。

我们现在希望的是,删除某些 set 操作,然后使得最终遍历这个树并执行对应的操作后,任意时刻都不会使 \(x= s\)

不难发现,从任意一棵树开始执行操作,最终从这个树中操作结束后,得到的值 \(x\),一定为子树中的某个 set y v\(v\)

注意到最后得到某个数 \(j\) 且中途不出现 \(x = s\) 代表着操作的结束,根据这个在树上做一个 dp,设 \(dp_{i,j}\) 表示在 \(i\) 的子树中,删除若干操作后最后得到的 \(x = j\) 的最小代价。

每次假如在树上碰到了叶子节点也就是 set y v 操作的时候,对于 \(j \neq y\) 的全部都要加上值 \(v\),对于 \(y\) 在特判一下 \(y \neq s\),如果 \(y\) 等于 \(s\) 的话,将其设为 \(\infty\),否则选择 \(j \neq y\) 的最小的一个值 \(dp_{i,j}\) 然后更新 \(dp_{i,y}\) 即可。

假如碰到了一个非叶子节点也就是 if y 操作的时候,首先需要判断一下是否能执行 \(y\) 操作,如果能的话,首先递归处理儿子子树内的答案,之后对儿子 \(dp_{to,j}\) 全体加上 \(dp_{i,j}\) 表示执行的前置代价,再将 \(dp_{i, y}\) 的值设为 \(dp_{to, y}\),因为此时会要考虑子节点的贡献,之后对于其他的 \(j\),将 \(dp_{i,j}\)\(dp_{to, j}\) 对应取 \(\min\) 即可。

不难发现这个是一个整体 \(dp\) 的过程,于是可以考虑对每个点开一颗动态开点值域线段树进行优化,然后算子节点对父节点的贡献的时候直接线段树合并即可,复杂度 \(n \log n\)

当然,也可以考虑使用启发式合并计算子节点对父节点的贡献,因为中途递归子节点的时候需要给子节点全局加上某个值 \(val\),此时需要在合并中对连通块维护懒惰标记来存储 \(val\),然后合并的时候如果是当前节点大,那么儿子直接暴力加 \(val\),不用管了,如果是当前儿子大,那么对于当前节点中每个点先 \(-dp_{i,j}\),然后全局加上一个 \(dp_{i,j}\) 就好了,而其他查询最小的值,额外再开一个 multiset 即可维护,复杂度 \(n\log^2 n\)

代码实现的是整体 \(dp\) 的代码。

// 德丽莎你好可爱德丽莎你好可爱德丽莎你好可爱德丽莎你好可爱德丽莎你好可爱
// 德丽莎的可爱在于德丽莎很可爱,德丽莎为什么很可爱呢,这是因为德丽莎很可爱!
// 没有力量的理想是戏言,没有理想的力量是空虚
#include <bits/stdc++.h>
#define LL long long
#define int long long
using namespace std;
char ibuf[1 << 15], *p1, *p2;
#define getchar() (p1 == p2 && (p2 = (p1 = ibuf) + fread(ibuf, 1, 1 << 15, stdin), p1==p2) ? EOF : *p1++)
inline int read() {
  char ch = getchar();  int x = 0, f = 1;
  while (ch < '0' || ch > '9')  {  if (ch == '-')  f = -1;  ch = getchar();  }
  while (ch >= '0' && ch <= '9')  x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
  return x * f;
}
void print(LL x) {
  if (x > 9)  print(x / 10);
  putchar(x % 10 + '0');
}
template<class T> bool chkmin(T &a, T b) { return a > b ? (a = b, true) : false; }
template<class T> bool chkmax(T &a, T b) { return a < b ? (a = b, true) : false; }
#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define repd(i, l, r) for (int i = (l); i >= (r); i--)
#define REP(i, l, r)  for (int i = (l); i < (r); i++)
bool o1;
const int N = 2e5 + 5, lim = 2e5, inf = 2e17;
int n, S, num, nex[N], first[N], v[N];
int st[N], tp;
struct node {  int id, x, y;  } a[N];
vector <int> ver[N];
int rt[N * 19], lc[N * 19], rc[N * 19], s[N * 19], tot, tag[N * 19];
void pushup(int p) {
  s[p] = min(s[lc[p]], s[rc[p]]);
}
void gettag(int p,int val) {
  s[p] += val;
  tag[p] += val;
}
void pushdown(int p) {
  if (!tag[p])  return;
  if (lc[p])  s[lc[p]] += tag[p], tag[lc[p]] += tag[p];
  if (rc[p])  s[rc[p]] += tag[p], tag[rc[p]] += tag[p];
  tag[p] = 0;
}
void change(int &p,int l,int r,int pos,int val) {
  if (!p)  p = ++tot;
  if (l == r) { s[p] = val;  return;   }
  pushdown(p);
  int mid = (l + r) >> 1;
  if (pos <= mid)  change(lc[p], l, mid, pos, val);
  else  change(rc[p], mid + 1, r, pos, val);
  pushup(p);
}
int ask(int p,int l,int r,int pos) {
  if (!p)  return inf;
  if (l == r)  return s[p];
  pushdown(p);
  int mid = (l + r) >> 1;
  if (pos <= mid)  return ask(lc[p], l, mid, pos);
  else  return ask(rc[p], mid + 1, r, pos);
}
int merge(int x,int y,int a,int b) {
  if (!x || !y)  return x + y;
  int nowrt = ++tot;
  if (a == b) {
    s[nowrt] = min(s[x], s[y]);
    return nowrt;
  }
  pushdown(x);
  pushdown(y);
  int mid = (a + b) >> 1;
  lc[nowrt] = merge(lc[x], lc[y], a, mid);
  rc[nowrt] = merge(rc[x], rc[y], mid + 1, b);
  pushup(nowrt);
  return nowrt;
}
int cnt = 0;
void dfs(int x) {
  change(rt[x], 0, lim, a[x].x, 0);
  for (auto to : ver[x]) {
    if (a[to].id == 1) {
      int now = s[rt[x]];
      gettag(rt[x], a[to].y);
      if (a[to].x != S)  change(rt[x], 0, lim, a[to].x, now);
      else  change(rt[x], 0, lim, a[to].x, inf);
    } else if (a[to].id == 2) {
      int val = ask(rt[x], 0, lim, a[to].x);
      if (val != inf) {
        dfs(to);
        gettag(rt[to], val);
        change(rt[x], 0, lim, a[to].x, inf);
        rt[x] = merge(rt[x], rt[to], 0, lim);
      }
    }
  }
}
bool o2;
void solve() {
  // cout << (&o2 - &o1) * 1.0 / 1024 / 1024 << "\n";
  cin >> n >> S;
  st[0] = n + 1;
  rep (i, 1, n) {
    string it ; 
    cin >> it;
    if (it == "set") {
      a[i].id = 1;
      cin >> a[i].x >> a[i].y;
      ver[st[tp]].push_back(i);
    } else if (it == "if") {
      a[i].id = 2;
      cin >> a[i].x;
      ver[st[tp]].push_back(i);
      st[++tp] = i;
    } else if (it == "end") {
      tp--;
    }
  }
  s[0] = inf;
  dfs(n + 1);
  cout << s[rt[n + 1]] << "\n";
}
signed main () {
#ifdef LOCAL_DEFINE
  freopen("1.in", "r", stdin);
  freopen("1.ans", "w", stdout);
#endif
  ios :: sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  int T = 1;  while (T--)  solve();
#ifdef LOCAL_DEFINE
  cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << " s.\n";
#endif
  return 0;
}
posted @ 2022-10-04 08:42  Pitiless0514  阅读(41)  评论(0编辑  收藏  举报