欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

牛客多校10 - Decrement on the Tree(边权转点权+思维)

发布时间:2024/4/11 编程问答 47 豆豆
生活随笔 收集整理的这篇文章主要介绍了 牛客多校10 - Decrement on the Tree(边权转点权+思维) 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

题目链接:点击查看

题目大意:给出一棵 n 个点组成的树,每条边上都有边权,现在可以进行数次操作,每次操作可以选择一条路径,使得路径上的权值减一,问最少需要进行多少次操作才能使得所有的边权变为 0 ,输出这个操作次数,再给出 m 次询问,每次询问会修改一条边权,每次需要回答修改边权后的答案

题目分析:读完题的第一感觉是树形dp然后用树剖+线段树优化,事实证明确实可以写,但我不会写

讲一下官方题解的做法吧,非常需要思维,首先需要将边权转换为点权,对于每次操作选取一条路径然后将其路径上的边权减一,对应过来就是将两个端点遍历一次,这样问题就转换为了,将所有边权变为 0 时,令每个端点被访问的次数之和最小

对于每个点计算贡献,就会发现有两种情况:假设 mmax 为与当前点相连的所有边中,权值最大的边权,sum 为与当前点相连的所有边权之和

  • mmax <= ( sum - mmax ):此时肯定有一种匹配方法使得边权两两互相匹配,换句话说,当前点不需要作为端点与其他端点形成路径,只需要作为中间点被经过就好,这样的贡献就是 sum%2 ,因为如果 sum 为奇数的话,会有一条边权失配,那么只能由该点作为端点一次
  • mmax > ( sum - mmax ):此时如果其余所有的边都与 mmax 匹配的话,最终还是会剩下 mmax - ( sum - mmax ) 的边权失配,那么需要当前点作为端点这么多次才行
  • 对于每次修改,只需要维护一个 multiset 实时计算贡献就好了

    最后的答案记得除以 2 ,因为当边权映射给点权后,是一条边权对应着两个端点,我们维护的 ans 是最少点的贡献,转换为边的贡献当然应该除以 2 了

    代码:
     

    #include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> #include<cassert> #include<bitset> #include<unordered_map> using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e5+100;multiset<int>st[N];LL sum[N];int x[N],y[N],w[N];void del(int p) {int x=::x[p],y=::y[p],w=::w[p];st[x].erase(st[x].find(w));st[y].erase(st[y].find(w));sum[x]-=w;sum[y]-=w; }void add(int p) {int x=::x[p],y=::y[p],w=::w[p];st[x].insert(w);st[y].insert(w);sum[x]+=w;sum[y]+=w; }LL cal(int p) {int mmax=*st[p].rbegin();if(mmax*2>sum[p])return mmax*2-sum[p];elsereturn sum[p]&1; }int main() { #ifndef ONLINE_JUDGE // freopen("data.in.txt","r",stdin); // freopen("data.out.txt","w",stdout); #endif // ios::sync_with_stdio(false);int n,m;scanf("%d%d",&n,&m);for(int i=1;i<n;i++){scanf("%d%d%d",x+i,y+i,w+i);add(i);}LL ans=0;for(int i=1;i<=n;i++)ans+=cal(i);printf("%lld\n",ans/2);while(m--){int pos,val;scanf("%d%d",&pos,&val);ans-=cal(x[pos])+cal(y[pos]);del(pos);w[pos]=val;add(pos);ans+=cal(x[pos])+cal(y[pos]);printf("%lld\n",ans/2);}return 0; }

     

    总结

    以上是生活随笔为你收集整理的牛客多校10 - Decrement on the Tree(边权转点权+思维)的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。