2024初秋集訓——提高組 #32

Yaosicheng124發表於2024-10-07

B. 序列刪除

題目描述

有一個長度為 \(2N\) 的序列 \(A\),其中 \(1\)\(N\) 恰好出現兩次。你每次可以選擇兩個相同的數 \(A_l,A_r(l<r)\) 並花費 \(r-l\) 的代價將其刪除。求將整個序列刪空的最小代價。

思路

有一個很顯然的貪心就是:每次取代價最小的兩個數刪除。所以我們按照代價排序,用樹狀陣列維護區間中未被刪除的數的數量即可。

空間複雜度 \(O(N)\),時間複雜度 \(O(N\log N)\)

程式碼

#include<bits/stdc++.h>
using namespace std;
using ll = long long;

const int MAXN = 200001, MAXV = (1 << 20);

struct Node {
  int l, r;
}s[MAXN];

int n, a[MAXN], l[MAXN], tr[MAXN];
ll ans;

void update(int p, int x) {
  for(; p <= 2 * n; tr[p] += x, p += (p & -p)) {
  }
}

int Getsum(int p) {
  int sum = 0;
  for(; p; sum += tr[p], p -= (p & -p)) {
  }
  return sum;
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n;
  for(int i = 1; i <= 2 * n; ++i) {
    cin >> a[i];
    if(!l[a[i]]) {
      l[a[i]] = i;
    }else {
      s[a[i]] = {l[a[i]], i};
    }
  }
  sort(s + 1, s + n + 1, [](const Node &a, const Node &b) -> bool {
    return a.r - a.l < b.r - b.l;
  });
  for(int i = 1; i <= n; ++i) {
    ans += s[i].r - s[i].l - Getsum(s[i].r) + Getsum(s[i].l);
    update(s[i].l, 1), update(s[i].r, 1);
  }
  cout << ans;
  return 0;
}

C. 史萊姆融合

題目描述

在一棵 \(N\) 個結點的樹上,有 \(k\) 只史萊姆,一開始在結點 \(id_i\),體積為 \(v_i\)。它們會如下行動:

  • 如果一隻史萊姆透過了邊權為 \(w\) 的邊,則其體積減 \(w\)。若其體積變為非正數,則其消失。
  • 如果多隻史萊姆待在同一個結點,則它們將會合併為體積為其體積之和的史萊姆。
  • 史萊姆最終都要到達一個結點 \(c\),但它們會按最優方案行動。

\(q\) 次查詢,每次查詢如果史萊姆要到達 \(c\),且你一開始可以讓 \(num(num\in \{0,1\})\) 個史萊姆消失時最終史萊姆的最小體積。

思路

我們先只考慮 \(c=1\) 的情況,那麼我們可以令 \(dp_{0/1,u}\) 表示在 \(u\) 子樹內刪除了 \(0/1\) 個史萊姆,最終子樹內的史萊姆到達 \(u\) 的體積。對於 \(dp_{0,u}\),顯然有轉移 \(dp_{0,u}=\sum \limits_{(v,w)\in son_u} \max(0,dp_{0,v}-w)\)。而對於 \(dp_{1,u}\),我們可以選擇刪掉這個結點本身的,也可以選擇一個兒子子樹刪,也就是 \(dp_{1,u}=\min (\max(0,dp_{0,u}-w),\min\limits_{v_0\in son_u} \{\max(0,dp_{1,v_0})+\sum \limits_{v\in son_u\and v\ne v_0}\max(0,dp_{0,v})\})\)

接著我們考慮換根,\(dp_{0,u}\) 的換根很簡單,但 \(dp_{1,u}\) 比較麻煩。如果 \(v\) 不是 \(dp_{1,u}\) 轉移時最小的那個 \(v_0\),那麼可以直接轉移。否則必須從次小值轉移過來,所以這裡還需要記錄次小值。

空間複雜度 \(O(N)\),時間複雜度 \(O(N+K+Q)\)

程式碼

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;

const int MAXN = 100001;
const ll INF = (ll)(1e18);

int n, k, q, a[MAXN], son[MAXN];
ll dp[3][MAXN];
vector<pii> e[MAXN];

void dfs(int u, int fa) {
  dp[0][u] = a[u];
  for(pii g : e[u]) {
    int v = g.first, w = g.second;
    if(v != fa) {
      dfs(v, u);
      dp[0][u] += max(0ll, dp[0][v] - w);
    }
  }
  dp[1][u] = dp[0][u] - a[u], dp[2][u] = INF, son[u] = u;
  for(pii g : e[u]) {
    int v = g.first, w = g.second;
    if(v != fa) {
      ll x = dp[0][u] - max(0ll, dp[0][v] - w) + max(0ll, dp[1][v] - w);
      if(x < dp[1][u]) {
        dp[2][u] = dp[1][u], dp[1][u] = x, son[u] = v;
      }else if(x < dp[2][u]) {
        dp[2][u] = x;
      }
    }
  }
}

void DFS(int u, int fa) {
  for(pii g : e[u]) {
    int v = g.first, w = g.second;
    if(v != fa) {
      ll v0 = dp[0][v], u0 = dp[0][u] - max(0ll, v0 - w);
      dp[0][v] += max(0ll, u0 - w);
      dp[1][v] += max(0ll, u0 - w);
      dp[2][v] += max(0ll, u0 - w);
      if(son[u] != v) {
        ll x = dp[0][v] - max(0ll, u0 - w) + max(0ll, dp[1][u] - max(0ll, v0 - w) - w);
        if(x < dp[1][v]) {
          dp[2][v] = dp[1][v], dp[1][v] = x, son[v] = u;
        }else if(x < dp[2][v]) {
          dp[2][v] = x;
        }
      }else {
        ll x = dp[0][v] - max(0ll, u0 - w) + max(0ll, dp[2][u] - max(0ll, v0 - w) - w);
        if(x < dp[1][v]) {
          dp[2][v] = dp[1][v], dp[1][v] = x, son[v] = u;
        }else if(x < dp[2][v]) {
          dp[2][v] = x;
        }
      }
      DFS(v, u);
    }
  }
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n >> k >> q;
  for(int i = 1, u, v, w; i < n; ++i) {
    cin >> u >> v >> w;
    e[u].emplace_back(v, w);
    e[v].emplace_back(u, w);
  }
  for(int i = 1, u; i <= k; ++i) {
    cin >> u >> a[u];
  }
  dfs(1, 0);
  DFS(1, 0);
  for(int i = 1, u, v; i <= q; ++i) {
    cin >> u >> v;
    cout << dp[v][u] << "\n";
  }
  return 0;
}

相關文章