https://ac.nowcoder.com/acm/contest/5675/C
每条边有一些权值,每次选择一条路径全部减1,最多选多少条路径。我们把问题转换为最少多少个路径的端点,路径数就是段点数/2,对于一个点,我们考虑他连接的所有边,如果mx<=sum/2,那么只要两条边能匹配起来,他本身就不用作为一个端点,那么此时作为端点的数量就是sum&1,如果mx>sum/2,那么最大的那条边是匹配不完的,作为端点的数量就是2*mx-sum。
修改维护的话就用个multiset搞搞就好
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxl=1e5+10;
int n,q;ll ans;
struct ed
{
int u,v,w;
}e[maxl];
ll sum[maxl];
multiset<int> s[maxl];
inline void prework()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n-1;i++)
{
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
sum[e[i].u]+=e[i].w;
sum[e[i].v]+=e[i].w;
s[e[i].u].insert(e[i].w);
s[e[i].v].insert(e[i].w);
}
}
inline int calc(int i)
{
int w=(*s[i].rbegin());
if(w<=sum[i]/2)
return sum[i]&1;
return 2*w-sum[i];
}
inline void mainwork()
{
ans=0;int p,w;
for(int i=1;i<=n;i++)
ans+=calc(i);
printf("%lld\n",ans/2);
for(int i=1;i<=q;i++)
{
scanf("%d%d",&p,&w);
ans-=calc(e[p].u);ans-=calc(e[p].v);
s[e[p].u].erase(s[e[p].u].lower_bound(e[p].w));
s[e[p].v].erase(s[e[p].v].lower_bound(e[p].w));
sum[e[p].u]-=e[p].w;sum[e[p].v]-=e[p].w;
e[p].w=w;
sum[e[p].u]+=e[p].w;sum[e[p].v]+=e[p].w;
s[e[p].u].insert(w);s[e[p].v].insert(w);
ans+=calc(e[p].u);ans+=calc(e[p].v);
printf("%lld\n",ans/2);
}
}
int main()
{
prework();
mainwork();
return 0;
}