题解 PK3585 【Accumulation Degree】
PK3585 Accumulation Degree
题目大意:
给一棵树,每条边有一个最大流量,问以哪个点为源点的流量最大,求最大流量。
solution:
能感觉出是用树形 \(\text{DP}\) 做,有一个朴素的做法:分别以每个点为根进行流水,时间复杂度 \(O(n^2)\) 。显然是不允许的,考虑下优化:
看这两个图:
以 \(1\) 作为根节点:
以 \(2\) 作为根节点:
我们发现在求完以 \(1\) 为根的 \(1\) 节点的最大流量后,以 \(2\) 为根中 \(1\) 子树是以 \(1\) 为根树的一部分,所以不用再流一遍,而是进行一些转化来更新 \(2\) 节点的最大流量。
根据刚才的思想,我们可以先以 \(1\) 节点为根,记录下 \(d_i\) 表示以 \(i\) 节点为根的子树的最大流量。设 \(f_i\) 为以节点 \(i\) 为根的最大流量(区分子树与树),对于 \(1\) 号点有 \(f_1=d_1\) (显然)。设当前节点为 \(x\) ,其父节点为 \(y\) 。
以 \(4\) 节点的转化为例:
此时已知 \(f_1=24,d_4=15\) ,要求 \(f_4\) ,很显然 \(f_4\) 包含 \(d_4\) ,那么另一部分怎么求呢?先用 \(f_1\) 把 \(4\) 右侧的流量减去,应该减什么呢?其实就是 \(\min(d_4,c_{1,4})\) ,这和“流”的描述相同。此时我们就知道 \(1\) 节点向下的流量了,那么 \(4\) 节点向 \(1\) 节点流量就是 \(\min(\,1\,现在的流量,c_{1,4}\) 。
转移方程:
- \(x\) 是叶子节点,如 \(2\) 节点:
这是 \(2\) 节点的最大流量就为 \(c(1,2)\) 。
细节处理:
- 在第一遍 \(\text{dfs}\) 求 \(d\) 数组时,叶子节点的 \(d\) 应为 \(\text{INF}\) 。
- LL。
代码
#include
#include
using namespace std;
typedef long long LL;
const int N=2e5+5,INF=0x7fffffff;
int rt,fa[N];
int hd[N],nt[N<<1],to[N<<1],cnt;LL ww[N<<1];
inline void add(int x,int y,int z){
ww[++cnt]=z,to[cnt]=y,nt[cnt]=hd[x],hd[x]=cnt;
}
int deg[N];
LL d[N],f[N],c[N],ans;
void dfs_1(int x){//求d数组
for(int i=hd[x];i;i=nt[i]){
int y=to[i];LL z=ww[i];
if(y==fa[x]) continue;
deg[x]++,fa[y]=x,c[y]=z,dfs_1(y);//c数组保存到y与x边的最大流量
d[x]+=min(d[y],z);
}
if(deg[x]==0) d[x]=INF;//度为0,d数组为INF
}
void dfs_2(int x){
if(x!=rt){//通过它的父亲来计算f数组
int y=fa[x];
f[x]=min(f[y]-min(d[x],c[x]),c[x]);
if(d[x]!=INF) f[x]+=d[x];
}
for(int i=hd[x];i;i=nt[i]){
int y=to[i];
if(y==fa[x]) continue;
dfs_2(y);
}
ans=max(ans,f[x]);
}
int main(){
int n; scanf("%d",&n);
for(int i=1,x,y,z;i