Jzoj4896 兔子

在一片草原上有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
",l);
}

原文地址:https://www.cnblogs.com/Extended-Ash/p/7800615.html