牛客周赛 Round 84

发布于 5 天前  30 次阅读


A.小红的陡峭值(一)

小红定义一个数组的陡峭值为:每两个相邻的元素,差值的绝对值之和。例如,数组${2,3,1}$​ 的陡峭值是 $∣2−3∣+∣3−1∣=3$​.
现在小红拿到了一个由 $3$​ 个整数组成的数组${a_1,a_2,a_3}$​,她希望你判断该数组的陡峭值是否为 $0$​,你能帮帮她吗?

直接模拟算一下

void solve() {
  int a, b, c;
  cin >> a >> b >> c;
  int ans = abs(a - b) + abs(b - c);
  if (ans == 0) {
    cout << "Yes";
  } else {
    cout << "No";
  }
}

B.小红的陡峭值(二)

小红定义一个数组的陡峭值为:每两个相邻的元素,差值的绝对值之和。例如,数组$ {2,3,1}$的陡峭值是 $∣2−3∣+∣3−1∣=3$

现在小红拿到了一个由 $n$ 个整数组成的数组 ${a_1,a_2,\dots,a_n}$,她希望你将该数组重新排列,使得数组的陡峭值尽可能小。请你输出小红有多少种不同的排列方案使得数组的陡峭值最小,并输出这个最小值。

两个方案被视为不同的,当且仅当存在一个位置 $i$使得两个方案中 $a_i$ 不同。

将数组排序后有

$b_1\leq b_2 \leq \cdots \leq b_n$

此时

$|b_2-b_1| + |b_3-b_2| +\cdots + |bn - b{n-1}| =-b_1 + b _2 - b_2 + b3 + \cdots - b{n-1} + b_n = b_n - b_1$

如果无序,例如将$b_3,b_1,b_2$则有 $b_3-b_1 + b _2 - b_1 =b_3 - b_2 + b_2-b_1 + b_2 - b_1 > b_n - b_n$

可证明当数组有序时最小,包括正序和逆序

但如果数组只有一个值,则答案为1种情况0

void solve() {
  int n;
  cin >> n;
  vector<int> a(n);

  for (int i = 0; i < n; i++) {
    cin >> a[i];
  }
  sort(all(a));
  int ans = 0;
  for (int i = 1; i < n; i++) {
    ans += abs(a[i] - a[i - 1]);
  }
  vector<int> b = a;
  rev(b);
  if (b == a) {
    cout << 1 << " ";
  } else
    cout << 2 << " ";
  cout << ans << endl;
}

小红的陡峭值(三)(hard)

小红定义一个字符串的陡峭值为:每两个相邻字符,Ascii 码差值的绝对值之和。例如,$\texttt{"bca"}$ 的陡峭值是 $|\texttt{c'}-\texttt{b'}|+|\texttt{a'}-\texttt{c'}|=1 + 2 = 3$。

现在小红拿到了一个长度为 $n$,仅由小写字母构成的字符串 $s$。她希望你求出这个字符串所有长度为 $k$的连续子串的陡峭值之和,你能帮帮她吗?

相邻两个字符的绝对值记录为$v_i$,则构造一个关于$v$的前缀数组

间隔为$k-1$的前缀差即为一个连续子串的和,求和得到结果

void solve() {
  int n, k;
  cin >> n >> k;
  string s;
  cin >> s;
  vector<ll> v = {0};
  for (int i = 1; i < s.size(); i++) {
    v.push_back(abs(s[i] - s[i - 1]) + v.back());
  }
  ll ans = 0;
  for (int i = k - 1; i < v.size(); i++) {
    ans += v[i] - v[i - k + 1];
  }
  cout << ans;
}

小红的陡峭值(四)

小红定义一棵树的陡峭值为:每一条边连接的两个相邻节点,编号差值的绝对值之和;若一棵树只有 $1$个节点,则该树的陡峭值为 $0$。例如,对于一棵由 $3$个节点组成的树,若节点 $2$和节点 $3$连边,节点 $3$和节点 $1$连边,则该树的陡峭值为 $∣2−3∣+∣3−1∣=3$
现在小红拿到了一棵由 $n$​个节点组成的树,她准备断掉一条树边,使得形成两棵树。小红希望这两棵树的陡峭值尽可能的接近。请你求出这两棵树陡峭值之差的绝对值的最小值。

对于一颗树,DFS可以计算当前节点为根节点,其所在子树的陡峭值和$v_x$.

枚举断掉每个节点$i$何其父节点$f_i$的边

则有新的子树的的陡峭值为$v_i$

剩余的部分的陡峭值为整体的和 - 去掉的子树-断掉的边的权重

$newt = v_0 - v_i +|i-f_i|$

其中$v_0$是根节点的值(可以是任意一个点。

vector<int> adj[N + 1];
ll v[N + 1];
bool vis[N + 1];
int f[N + 1];
ll S = 0;
int n;

ll dfs(int x, int p) {
  f[x] = p;
  vis[x] = true;
  ll res = 0;
  for (int c : adj[x]) {
    if (!vis[c]) {
      ll lr = dfs(c, x);
      res += (lr + llabs((ll)x - c));
    }
  }
  v[x] = res;
  return res;
}

void solve() {
  cin >> n;
  for (int i = 1; i <= n; i++) {
    adj[i].clear();
    vis[i] = false;
    v[i] = 0;
  }
  for (int i = 0; i < n - 1; i++) {
    int u, v;
    cin >> u >> v;
    adj[u].push_back(v);
    adj[v].push_back(u);
  }

  ll rs = dfs(1, 0);
  S = rs;

  ll ans = LLONG_MAX;
  for (int x = 2; x <= n; x++) {
    int p = f[x];
    ll c = llabs(p - x);
    ll ori = S - v[x] - c;
    ll newt = v[x];
    ans = min(ans, abs(ori - newt));
  }

  cout << ans << "\n";
}

小红的陡峭值(五)(hard)

小红定义一个数组的陡峭值为:每两个相邻的元素,差值的绝对值之和。例如,数组 ${2,3,1}$的陡峭值是 $∣2−3∣+∣3−1∣=3$。
现在小红拿到了一个由 $n$ 个整数组成的数组${a_1,a_2,\dots,a_n}$,然后蒙眼打乱了数组。她希望你求出该数组陡峭值的期望。请将答案对$10^9+7$取模后输出。

陡峭值可以分解成两个元素相邻的组合的差值的求和

对于两个数$a_i,a_j$相邻,首先固定这两个数,剩下$n-1$个空的排列,同时两个数内部还有两个组合

则两个数相邻的情况有$2\times (n-1)!$种,在全部的情况下,有$n!$个排列

因此两个数$a_i,a_j$相邻的概率为$\frac{2\times (n-1)!}{n} = \frac{2}{n}$​

简化一下只考虑正序,不考虑reversed结果,则直接去掉一半组合

则期望为$E = \frac{2}{n}\sum (a_j-a_j)$​.

因此只需要求$1\leq i < j <n$时候$\sum (a_j-a_j)$

当$n=18$的时候直接$n^2$暴力

当n大了后可以用前缀和优化

$\sum_{i=1}^{j-1}a_j - a_i = (j-1)\cdot aj -\sum{i=1}^{j-1}a_i$

直接前缀和维护,计算这个式子就得到了对应的分子,分母则是$n$

求逆元做乘积得到结果

ll qpow(ll a, ll b, ll m) {
  ll res = 1 % m;
  a %= m;
  while (b > 0) {
    if (b & 1) res = (res * a) % m;
    a = (a * a) % m;
    b >>= 1;
  }
  return res;
}

ll inv(ll a, ll m) { return qpow(a, m - 2, m); }

void solve() {
  int n;
  cin >> n;
  vector<ll> arr(n);
  for (int i = 0; i < n; i++) {
    cin >> arr[i];
  }
  sort(all(arr));
  vector<ll> pre(n + 1, 0ll);
  for (int i = 1; i <= n; i++) {
    pre[i] = pre[i - 1] + arr[i - 1];
  }
  ll cnt = 0;
  for (int j = 2; j <= n; j++) {
    ll tmp = arr[j - 1];
    ll lc = j - 1;
    cnt = (cnt + 1ll * lc * tmp - pre[j - 1]) % mod;
  }
  ll p = (2ll * cnt) % mod;
  ll iq = inv(n, mod);
  ll ans = (1ll * p * iq) % mod;

  cout << ans;
}

一天一个🍑!