• 2020暑假牛客多校10 C -Decrement on the Tree (边权转点权处理)


    C Decrement on the Tree

    参考博客

    题目:

    一棵树每次选择一条路径将路径上的边权都减1,问最少多少次操作后所有边权变成0。

    题解:

    看了好几篇博客才明白了,这道题的做法是将对边权的处理转变成去想对点的处理,算各点的贡献。之所以可以这样做是基于给一条边的边权-1,相当于访问这条边所连2个点各1次。

    如果不转换思路去考虑各边对结果的贡献,边的边权知道,两点知道,其他的如选的路径每次是什么都不清楚,所以会有一种思路很”窄“的感觉。但是换作考虑点的贡献,对每一个点,它连着好几条边。任意两个边都可以形成路径,将所有边减为0相当于这个点所连的边考虑边的权值关系下相互匹配,至于什么关系下面讨论,反正思路是很开阔的。这应该算是一种处理图问题的重要思路,当然有时候也把点的权值问题转为边的问题。

    讨论一个点各边权值关系的处理:

    (1) 最好想情况是 一个边权超级大,那么其他边都和这一边组成路径把其他边消为0,最后肯定这条边无法”蹭“别的路径去消除,只能自己消除,这就是当前点的贡献。设当前点为u, 其所连的总边权为sum , 超级大边权为maxx 。得该点贡献为:(sum-maxx是除maxx外其它边权和)

    [maxx - (sum - maxx) = 2*maxx-sum ]

    容易发现maxx >= sum / 2 时就属于情况(1) 。 感性理解就是其他边权和加起来都不能消除完maxx时是情况(1)

    (2) 如果maxx < sum / 2, 且u相连边总边权sum 为偶数时,肯定可以相互匹配完。贡献为0. sum为奇数时,最后一定会剩余1需要u作为贡献去处理掉。

    这样遍历一遍每个结点就可以求出中贡献,但是一个边对应2个点,所以算的结果应该除2才是对边得最少操作数。

    至于q次询问,每次O(1) 修改一下p边所连点得信息,重新算一下该边相连两点得贡献就可以算出答案。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define rep(i, a, n) for(int i = a; i <= n; ++ i);
    #define per(i, a, n) for(int i = n; i >= a; -- i);
    typedef long long ll;
    const int N = 2e6+ 5;
    const int mod = 998244353;
    const double Pi = acos(- 1.0);
    const int INF = 0x3f3f3f3f;
    const int G = 3, Gi = 332748118;
    ll qpow(ll a, ll b) { ll res = 1; while(b){ if(b) res = (res * a) % mod; a = (a * a) % mod; b >>= 1;} return res; }
    ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
    ll lcm(ll a, ll b) { return a * b / gcd(a, b);}
    bool cmp(int a, int b){ return a > b;}
    //
    
    int n, q; ll res;
    multiset<int> sol[N];
    int x[N], y[N];
    ll w[N], val[N];
    
    int cal(int i){	//计算每点贡献
        int tp = *sol[i].rbegin();
        if(2 * tp >= val[i]) return 2 * tp - val[i];
        else return val[i] & 1;
    }
    
    int main()
    {
        scanf("%d%d",&n,&q);
        for(int i = 1; i < n; ++ i){
            scanf("%d%d%lld",&x[i],&y[i],&w[i]);
            val[x[i]] += w[i], val[y[i]] += w[i];
            sol[x[i]].insert(w[i]); sol[y[i]].insert(w[i]);
        }
        for(int i = 1; i <= n; ++ i) res += cal(i);
        printf("%lld
    ",res / 2);
        
        while(q --){    //q次询问
            int p; ll tw; scanf("%d%lld",&p,&tw);
            res -= cal(x[p]) + cal(y[p]);
            val[x[p]] -= w[p]; val[y[p]] -= w[p];
            sol[x[p]].erase(sol[x[p]].find(w[p])); 
            sol[y[p]].erase(sol[y[p]].find(w[p]));
            
            w[p] = tw;
            val[x[p]] += tw; val[y[p]] += tw;
            sol[x[p]].insert(tw); sol[y[p]].insert(tw);
            res += cal(x[p]) + cal(y[p]);
            printf("%lld
    ",res / 2);
        }
        return 0;
    }
    
  • 相关阅读:
    回文字符串问题
    Linux添加nfs共享存储盘
    解读nginx配置
    制作自己的nginx rpm包
    linux编译安装时常见错误解决办法
    redis单机及集群安装
    nginx ssl
    vsftp配置详解
    Linux-文件系统的简单操作
    Linux-Vim编辑器
  • 原文地址:https://www.cnblogs.com/A-sc/p/13502719.html
Copyright © 2020-2023  润新知