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;
}