P6594 [YsOI2020] 换寝室 —— 树形dp+二分答案

· · 个人记录

题意:给定一棵树,每条边有边权,割掉一些边,使得被割掉的边边权和不超过 k ,最小化剩余连通块点权极差的最大值

13pts

## 21pts 枚举每一条权值不为零的边打开或关闭,指数级别复杂度 ## 正解 考虑dp 原本想到背包,将权值作为体积,但是割去每条边的价值会**不断变化,并不显然** $$ _{//接下来均为看了题解之后的} $$ - - - 我们发现,答案是随着k的增加**单调递减**的 发现满足单调性,考虑**二分答案** 二分答案 $res$ ,然后思考如何检验答案合法性 这个时候就可以发现代价很好dp了 设状态 $dp_{u,x}$ 为 $u$ 的子树都被合法地划分成若干个连通块, $u$ 此时属于以 $x$ 为最小值的联通块的最小代价 容易知道,只要点权 $val_u$ 在 $[val_x,val_x+res]$ 之内,它就有可能在其的连通块中;反之则不可能 节点对于每个其的每个孩子,都有割和不割两种选择(即加入连通块或断开),于是可以得到状态转移方程 $$ dp_{u,x}=\sum_{v \in u's son} min(dp_{v,x},mn_v+cost_{u,v}) $$ 其中 $cost_{u,v}$ 为断开 $u$ 与 $v$ 这条边的代价, $mn$ 为一个点能凑出的所有合法情况的最小值 (其实很好理解,我都断开了,它肯定无所谓怎么连,代价怎么小怎么来) AC Code: ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN=805; const int inf=0x3f3f3f3f; int n,m,k; int dep[MAXN],fa[MAXN],siz[MAXN],hson[MAXN]; vector<int> edge[MAXN]; void dfs1(int x){ dep[x]=dep[fa[x]]+1; siz[x]=1; for(auto v:edge[x]){ if(v==fa[x]) continue; fa[v]=x; dfs1(v); siz[x]+=siz[v]; if(!hson[x] || siz[v]>siz[hson[x]]) hson[x]=v; } }// int top[MAXN]; void dfs2(int x,int h){ top[x]=h; if(hson[x]) dfs2(hson[x],h); for(auto v:edge[x]){ if(v==fa[x]) continue; if(v==hson[x]) continue; dfs2(v,v); } }// int LCA(int x,int y){ while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]]) swap(x,y); x=fa[top[x]]; } if(dep[x]<dep[y]) swap(x,y); return y; }// int delta[MAXN],cnt[MAXN]; bool block[MAXN]; void update(int x,int y){ delta[x]++;delta[y]++;delta[LCA(x,y)]-=2; }// int FirstDFS(int x){ int sum=0; for(auto v:edge[x]){ if(v==fa[x]) continue; sum+=FirstDFS(v); } sum+=delta[x]; if(!sum) block[x]=1; cnt[x]=sum; return sum; }// int difference[MAXN]; int mx[MAXN],mn[MAXN]; int GetUnsatisfaction(int x){ int res=0; mx[x]=mn[x]=difference[x]; for(auto v:edge[x]){ if(v==fa[x]) continue; res=max(res,GetUnsatisfaction(v)); if(block[v]) continue; mx[x]=max(mx[x],mx[v]); mn[x]=min(mn[x],mn[v]); } res=max(res,mx[x]-mn[x]); return res; }// int ans; int dp[MAXN][MAXN]; void DP(int x,int rs){ for(auto v:edge[x]){ if(v==fa[x]) continue; DP(v,rs); } for(int i=1;i<=n;++i){ dp[x][i]=inf; if(difference[x]<=difference[i]+rs && difference[x]>=difference[i]) dp[x][i]=0; } mn[x]=inf; for(int i=1;i<=n;++i){ if(dp[x][i]>=inf) continue; for(auto v:edge[x]){ if(v==fa[x]) continue; dp[x][i]+=min(dp[v][i],mn[v]+cnt[v]); } mn[x]=min(mn[x],dp[x][i]); } }// //void Getans(int i,int nk){ // if(i>n) return ; // if(block[i]){ // Getans(i+1,nk); // return; // }if(nk<0){ // return; // } // Getans(i+1,nk); // // if(nk-cnt[i]<0) return ; // block[i]=1; // ans=min(ans,GetUnsatisfaction(1)); // Getans(i+1,nk-cnt[i]); // block[i]=0; //}// int erf(int l,int r){ int mid=l+r>>1; while(l<r){ DP(1,mid); if(mn[1]<=k) r=mid; else l=mid+1; mid=l+r>>1; } return l; }// void work(){ int x,y; scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=n;++i) scanf("%d",&difference[i]); for(int i=1;i<n;++i){ scanf("%d%d",&x,&y); edge[x].push_back(y); edge[y].push_back(x); } dfs1(1); dfs2(1,1); while(m--){ scanf("%d%d",&x,&y); update(x,y); } FirstDFS(1); int res=GetUnsatisfaction(1); if(k==0){ printf("%d",res); return ; } ans=erf(0,1e9+5); printf("%d",ans); }// int main(){ work(); return 0; } ```