牛客第十场自闭

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;

}
原文地址:https://www.cnblogs.com/ruanbaiQAQ/p/13492727.html