标签:空格 script highlight 直接 out def ons 差分约束 i++
Discription
有一棵以1为根的树,他有n个结点,用1到n编号。第i号点有一个值vi。
现在可以对树进行如下操作:
步骤1:在树中选一个连通块,这个连通块必须包含1这个结点。
步骤2:然后对这个连通块中所有结点的值加1或者减1。
问最少要经过几次操作才能把树中所有结点都变成0。
注意:步骤1与步骤2合在一起为一次操作。
Input
单组测试数据。
第一行有一个整数n(1 ≤ n ≤ 10^5)
接下来n-1行,每行给出 ai 和 bi (1 ≤ ai, bi ≤ n; ai ≠ bi),表示ai和bi之间有一条边,输入保证是一棵树。
最后一行有n个以空格分开的整数,表示n个结点的值v1, v2, ..., vn (|vi| ≤ 10^9)。
Output
输出一个整数表示最少的操作步数。
Sample Input
3
1 2
1 3
1 -1 1
Sample Output
3
题解见注释(真的不知道我怎么想到差分约束的2333,网上全是用树上dp做的。。。)
/* 考虑差分约束。 1.设每个点i被add(i)次加操作涉及到,被dec(i)次减操作涉及到, 那么: add(i) - dec(i) + w[i] = 0. 2.把dec用add表示后,每个点有两个限制: (1). add(i) >= max{0,-w[i]} (2). add(i) <= add(fa) + min{0,w[fa]-w[i]} 然后直接跑一个最短路,2*add(1) + w[1] 就是答案. */ #include<bits/stdc++.h> #define ll long long #define pb push_back using namespace std; const int maxn=100005; vector<int> g[maxn]; int ne[maxn*5],to[maxn*5],num; int hd[maxn],val[maxn*5],n,m,w[maxn]; bool iq[maxn]; ll d[maxn]; inline void add(int x,int y,int z){ to[++num]=y,ne[num]=hd[x],hd[x]=num,val[num]=z; } void dfs(int x,int fa){ add(fa,x,min(0,w[fa]-w[x])); for(int i=g[x].size()-1,TO;i>=0;i--){ TO=g[x][i]; if(TO==fa) continue; dfs(TO,x); } } inline void build(){ for(int i=1;i<=n;i++) add(i,0,min(0,w[i])); dfs(1,1); } inline void spfa(){ queue<int> q; for(int i=0;i<=n;i++) q.push(i),iq[i]=1; int x; while(!q.empty()){ x=q.front(),q.pop(); for(int i=hd[x];i;i=ne[i]) if(d[x]+(ll)val[i]<d[to[i]]){ d[to[i]]=d[x]+(ll)val[i]; if(!iq[to[i]]) q.push(to[i]),iq[to[i]]=1; } iq[x]=0; } for(int i=1;i<=n;i++) d[i]-=d[0]; } int main(){ scanf("%d",&n); int uu,vv; for(int i=1;i<n;i++){ scanf("%d%d",&uu,&vv); g[uu].pb(vv),g[vv].pb(uu); } for(int i=1;i<=n;i++) scanf("%d",w+i); build(); spfa(); printf("%lld\n",d[1]*2+(ll)w[1]); return 0; }
标签:空格 script highlight 直接 out def ons 差分约束 i++
原文地址:https://www.cnblogs.com/JYYHH/p/8807625.html