SVUCPC

Yaosicheng124發表於2024-09-26

GYM 105264 C

題目描述

給定一個長度為 \(N\) 的陣列 \(A\),每次你可以令 \(A_i \leftarrow A_i+1\)\(A_i-1\)。求進行至多 \(k\) 次操作後 \(A\) 中最少不同元素數量。

思路

首先對 \(A\) 進行排序。

\(dp_{i,j}\) 表示考慮前 \(i\) 個數,有 \(j\) 個不同的值時最多還能剩餘幾次操作。

很明顯有 \(dp_{i,j}=\max \limits_{1\le l\le x\le i} \{dp_{l-1,j-1}+sum_i - sum_{x}-(i-x)\cdot A_x + (x - l)\cdot A_x - sum_{x-1}+sum_l\}\)

這個式子中似乎只能最佳化 \(x\),所以我們考慮怎麼最佳化。

這裡的 \(x\) 實際上就是在列舉變成哪個值,假設一開始我們不用下標而用值來表示。每次令 \(x\leftarrow x+1\),這樣做會使答案 \(+\le x的個數-> x的個數\),一開始這個增量是負的,逐漸變大。而當增量 \(=0\) 時也就是最小值。

而哪個地方 \(=0\) 呢?此時 \(\le x的個數=> x的個數\),也就是在 \(\lceil \frac{l+i}{2} \rceil\) 處。

空間複雜度 \(O(N^2)\),時間複雜度 \(O(N^3)\)

程式碼

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

const int MAXN = 301;

int T, n, a[MAXN], ans;
ll k, sum[MAXN], dp[MAXN][MAXN];

void Solve() {
  cin >> n >> k;
  for(int i = 1; i <= n; ++i) {
    cin >> a[i];
  }
  sort(a + 1, a + n + 1);
  for(int i = 1; i <= n; ++i) {
    sum[i] = sum[i - 1] + a[i];
  }
  for(int i = 0; i <= n; ++i) {
    for(int j = 0; j <= n; ++j) {
      dp[i][j] = -1;
    }
  }
  dp[0][0] = k;
  for(int i = 1; i <= n; ++i) {
    for(int j = 1; j <= n; ++j) {
      for(int l = 1; l <= i; ++l) {
        dp[i][j] = max(dp[i][j], dp[l - 1][j - 1] + sum[(l + i + 1) / 2] + 1ll * a[(l + i + 1) / 2] * (i - (l + i + 1) / 2) - 1ll * a[(l + i + 1) / 2] * ((l + i + 1) / 2 - l) + sum[(l + i + 1) / 2 - 1] - sum[l - 1] - sum[i]);
      }
    }
  }
  for(int i = 1; i <= n; ++i) {
    if(dp[n][i] >= 0) {
      cout << i << "\n";
      return;
    }
  }
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  for(cin >> T; T--; Solve()) {
  }
  return 0;
}

GYM 105264 E

題目描述

給定一個 \(N\) 個點的樹,你要從中選出一個大小為 \(k\) 的子樹出來,求這個子樹的最小直徑。

思路

由於此題允許 \(O(N^2)\) 的時間複雜度,所以考慮列舉子樹的中心。

接著以該中心為根向下搜出深度為 \(i\) 的結點數 \(cnt_i\),接著列舉直徑長度除 \(2\)。由於這裡直徑可能長度為偶數,則此時中點在一條邊上。所以我們把每條邊也看作一個點(但不統計在 \(cnt\) 中)。找到第一個 \(\sum \limits_{i=0}^x cnt_i \ge k\)\(x\),並統計答案即可。

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

程式碼

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

const int MAXN = 2001;

int n, k, dep[MAXN], cnt[MAXN], ans;
vector<int> e[MAXN];

void dfs(int u, int fa) {
  dep[u] = dep[fa] + 1;
  cnt[dep[u]] += (u <= n);
  for(int v : e[u]) {
    if(v != fa) {
      dfs(v, u);
    }
  }
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n >> k;
  for(int i = 1, u, v; i < n; ++i) {
    cin >> u >> v;
    e[u].push_back(n + i);
    e[n + i].push_back(v);
    e[v].push_back(n + i);
    e[n + i].push_back(u);
  }
  ans = 2 * n;
  for(int i = 1; i < 2 * n; ++i) {
    for(int j = 1; j <= 2 * n; ++j) {
      dep[j] = cnt[j] = 0;
    }
    dfs(i, 0);
    int res = 0;
    for(int j = 1; j <= 2 * n; ++j) {
      res += cnt[j];
      if(res >= k) {
        ans = min(ans, j);
        break;
      }
    }
  }
  cout << ans;
  return 0;
}