题意:
给一棵树,有
n
n
个点,其中个关键点, 求一种分割方案(断掉一些边)使得每个联通块至少有
1
1
个关键点,且使得最大块最小。
题解:
二分块的限制 , 然后记表示与 i i <script type="math/tex" id="MathJax-Element-284">i</script>联通的最小块大小, 以及这个最小块是否含黑点,贪心做树形DP即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int RLEN=1<<18|1;
inline char nc() {
static char ibuf[RLEN],*ib,*ob;
(ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
return (ib==ob)?-1:*ib++;
}
inline int rd() {
char ch=getchar(); int i=0,f=1;
while(!isdigit(ch)) {if(ch=='-')f=-1; ch=getchar();}
while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=getchar();}
return i*f;
}
const int N=2e5+50;
int n,k,col[N],col2[N],lim,f[N];
vector <int> edge[N];
inline bool dfs(int x,int fa) {
LL sum0=0; int mn1=1e9;
for(auto v:edge[x]) {
if(v==fa) continue;
if(!dfs(v,x)) return false;
if(col2[v]) {
mn1=min(mn1,f[v]);
} else {
sum0+=f[v];
}
}
if(col[x]) {
if(sum0+1<=lim) {
f[x]=sum0+1;
col2[x]=1;
} else return false;
} else {
if(sum0+1+mn1<=lim) {
col2[x]=1;
f[x]=sum0+1+mn1;
} else if(sum0+1<=lim){
col2[x]=0;
f[x]=sum0+1;
} else return false;
}
return true;
}
inline bool check(int v) {
lim=v;
if(!dfs(1,0)) return false;
return col2[1];
}
int main() {
n=rd(), k=rd();
for(int i=1;i<n;i++) {
int x=rd(), y=rd();
edge[x].push_back(y);
edge[y].push_back(x);
}
for(int i=1;i<=k;i++) col[rd()]=1;
int l=1,r=1e9,ans;
while(l<=r) {
int mid=(l+r)>>1;
if(check(mid)) ans=mid, r=mid-1;
else l=mid+1;
}
cout<<ans;
}