1. 程式人生 > 實用技巧 >[國家集訓隊]阿狸和桃子的遊戲

[國家集訓隊]阿狸和桃子的遊戲

對於兩點之間的邊是不好處理的,但基於觀察可以發現:雙方都只會將點染色,因此我們的想法是將邊權放到點權上去然後構造出一種選擇點權的方案與原問題等價。

同時,因為對於一條邊而言它連線的兩點代價是一致的,因此下放點權的過程應該滿足對稱性。

此時基於觀察可以發現:

  • \(\forall (u, v, w)\) 如果下放到 \(u, v\) 的點權滿足對稱性即相等,則如果 \(u, v\) 沒有同時被一個人選擇那麼邊權將會恰好抵消滿足題意。

那麼我們只需要調整下方的大小使得當 \(u, v\) 同時被選時獲得的權值恰好邊權即可。

不難發現有且僅有 \(\frac{w}{2}\) 滿足要求。

但因為 \(w\)

可能為奇數不方便計算,因此我們開始將所有權值擴大一倍最後將答案減半即可。

於是問題就被轉化為:先手後手每次可以取一個點獲得這個點的點權,問對抗博弈後先手減後手的得分。

不難發現此時先後手的策略都一定是取每次剩下的最大值,排序一遍即可,複雜度 \(O(n \log n + m)\)

#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = l; i <= r; ++i)
const int N = 10000 + 5;
int n, m, u, v, w, ans, a[N];
int read() {
    char c; int x = 0, f = 1;
    c = getchar();
    while (c > '9' || c < '0') { if(c == '-') f = -1; c = getchar();}
    while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
signed main() {
    n = read(), m = read();
    rep(i, 1, n) a[i] = read() * 2;
    rep(i, 1, m) u = read(), v = read(), w = read(), a[u] += w, a[v] += w;
    sort(a + 1, a + n + 1);
    for (int i = n, j = 1; i >= 1; --i, j = -j) ans += j * a[i];
    printf("%d", ans / 2);
    return 0;
}