1. 程式人生 > 實用技巧 >牛客第十場自閉

牛客第十場自閉

C-Decrement on the Tree

統計每個點連線邊的邊權和以及最大邊權,然後進行如下貪心:

ll find(ll x)//將邊權存到了multiset<ll>s[x]
{
    ll Max = *(--s[x].end());//x節點連的最大邊
    if (Max >= sum[x] - Max)
        return 2 * Max - sum[x];
    return sum[x] % 2;
}

這樣找到的ans是比答案大1倍的,因為每一條邊連向了兩個點,那麼每一條邊都被統計了2次答案,所以ans最後還要/=2

寫了詳細註釋:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
const int maxn = 1e5 + 10;
const ll inf = 1e17;
ll mod = 998244353;

multiset<ll>s[maxn];

ll a[maxn], b[maxn], w[maxn], sum[maxn];

ll find(ll x)
{
    ll Max = *(--s[x].end());//x節點連的最大邊
    //cout << x << " " << Max << endl;;
    if (Max >= sum[x] - Max)
        return 2 * Max - sum[x];
    return sum[x] % 2;
}

int main()
{
    fastio;
    int n, q;
    cin >> n >> q;
    for (int i = 1; i < n; i++)
    {
        cin >> a[i] >> b[i] >> w[i];//由於q是按邊的序號修改的,那就得按序號存邊
        sum[a[i]] += w[i], sum[b[i]] += w[i];//統計每個點的邊權和
        s[a[i]].insert(w[i]);//將邊權插入每個點對應的multiset
        s[b[i]].insert(w[i]);
    }
    ll ans = 0;
    for (int i = 1; i <= n; i++)
    {
        ans += find(i);//統計答案
        //cout << ans << endl;
    }
    cout << ans / 2 << endl;//每個邊被統計了2次,所以/=2
    while (q--)
    {
        ll x, y;
        cin >> x >> y;//修改就是把對應的邊還原回來,然後改成需要的邊權,再統計一下這兩點的答案
        ans -= find(a[x]) + find(b[x]);
        s[a[x]].erase(s[a[x]].find(w[x]));
        s[b[x]].erase(s[b[x]].find(w[x]));
        sum[a[x]] -= w[x] - y;
        sum[b[x]] -= w[x] - y;
        w[x] = y;
        s[a[x]].insert(y);
        s[b[x]].insert(y);
        ans += find(a[x]) + find(b[x]);
        cout << ans / 2 << endl;
    }
    return 0;

}