在一片草原上有N个兔子窝,每个窝里住着一只兔子,有M条路径连接这些窝。更特殊地是,至多只有一个兔子窝有3条或更多的路径与它相连,其它的兔子窝只有1条或2条路径与其相连。换句话讲,这些兔子窝之前的路径构成一张N个点、M条边的无向连通图,而度数大于2的点至多有1个。
兔子们决定把其中K个兔子窝扩建成临时避难所。当危险来临时,每只兔子均会同时前往距离它最近的避难所躲避,路程中花费的时间在数值上等于经过的路径条数。为了在最短的时间内让所有兔子脱离危险,请你安排一种建造避难所的方式,使最后一只到达避难所的兔子所花费的时间尽量少。
一张特殊的图,看到这种问法就考虑二分答案
由于存在一个‘根’,我们考虑去掉这个根后如何计算
整个图变成了许多条链,我们假设当前二分的答案为mid,这条链长度为len,那么显然只需要[len/(2mid+1)]个节点即可
让后考虑如何加入这个根的影响,我们先dfs一次计算出哪些节点如果被选取可以覆盖到根(然后就可以删掉根计算答案)让后枚举选取哪个,并将根删掉来统计答案
这样总复杂度为O(N^2*lgN)
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#define N 1010
using namespace std;
int n,m,k,cnt=0,sz,d[N],v[N][3],rt=1;
vector<int> G[N];
void dfs(int x,int p,int dp,int o){
v[x][o]++; sz++;
if(dp)
for(int u,i=0;i<G[x].size();++i)
if((u=G[x][i])!=p && !v[u][o] && ((o<2)||(!v[u][1]))) dfs(u,x,dp-1,o);
}
int main(){
freopen("rabbit.in","r",stdin);
freopen("rabbit.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);
for(int x,y,i=1;i<=m;++i){
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
d[x]++; d[y]++;
}
for(int i=2;i<=n;++i) if(d[i]>2) rt=i;
int l=0,r=n,tot;
for(int m,A;l<r;){
m=l+r>>1; A=1<<30;
memset(v[0],0,n<<2);
dfs(rt,0,m,0);
for(int i=1;i<=n;++i)
if(v[i][0]){
for(int j=1;j<=n;++j) v[j][1]=v[j][2]=0;
memset(v[2],0,n<<2);
dfs(i,0,m,1); tot=1;
for(int j=1;j<=n;++j)
if(!v[j][1]&&!v[j][2]){
sz=0; dfs(j,0,n,2);
tot+=(--sz)/(m<<1|1)+1;
}
A=min(A,tot);
}
if(A<=k) r=m; else l=m+1;
}
printf("%d\n",l);
}