[bzoj 2097]奶牛健美操
题目描述
对于一棵n个点的树,删除k条边,使得所有联通块直径最大值最小
题解
首先二分联通块直径最大值的最小值。
那么这个能否达成的判定变成了一个类似树形dp的东西
对于一个子树,删除一条边可以删除整个子树
对于所有子树,从到达最优答案时的深度,最大的开始删除,如果当前最大值+次大值<=k时退出循环
那么有一个二分的log 加上每次转移的sort,复杂度就是
#include<map> #include<stack> #include<queue> #include<cstdio> #include<string> #include<vector> #include<cstring> #include<complex> #include<iostream> #include<assert.h> #include<algorithm> using namespace std; #define inf 1001001001 #define infll 1001001001001001001LL #define ll long long #define dbg(vari) cerr<<#vari<<" = "<<(vari)<<endl #define gmax(a,b) (a)=max((a),(b)) #define gmin(a,b) (a)=min((a),(b)) #define Ri register int #define gc getchar() #define il inline il int read(){ bool f=true;Ri x=0;char ch;while(!isdigit(ch=gc))if(ch=='-')f=false;while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=gc;}return f?x:-x; } #define gi read() #define FO(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout); struct edge{ int to,next; }e[233333]; int last[100005],ecnt,cnt,n,k,lb,rb,mid,ans; int f[100005],_a,a[100005]; int dfs(int x,int fa=0){ f[x]=0; //... for(int i=last[x];i;i=e[i].next)if(e[i].to!=fa)dfs(e[i].to,x); _a=a[0]=0; for(int i=last[x];i;i=e[i].next)if(e[i].to!=fa)a[++_a]=f[e[i].to]+1; sort(a+1,a+_a+1); while(_a&&a[_a]+a[_a-1]>mid)_a--,ans++; f[x]=a[_a]; } bool chk(){ ans=0; dfs(1); return ans<=k; } int main(){ n=gi;k=gi; for(int i=1;i<n;i++){ int a=gi,b=gi; e[++ecnt]=(edge){b,last[a]};last[a]=ecnt; e[++ecnt]=(edge){a,last[b]};last[b]=ecnt; } int ans=n,lb=1,rb=n; while(lb<=rb){ mid=(lb+rb)/2; if(chk()){ ans=mid; rb=mid-1; }else{ lb=mid+1; } } printf("%d",ans); return 0; }