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\n",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\n",res / 2);
    }
    return 0;
}
posted @ 2020-08-14 15:53  A_sc  阅读(185)  评论(0编辑  收藏  举报