题目链接: https://codeforces.com/gym/100519/problem/H
题意:
你现在有一个特殊的无向图,这个图除了最多某一个点外,其余的点的度数均为二,每个点上都有一只兔子,现在灾难要来了,你可以在 k k k 个点上打洞,每只兔子会往最近的洞跑,问你跑的最远的兔子的最短距离是多少。
做法:
二分距离,重点是在已知当前距离 L L L 情况下的判断。
很明显我们可以知道距离 L L L 是个很特殊的距离,假如我们在中心点放一个点,那么 L L L 范围内的点都可以被消除,并且我们只需要一个点,比较赚。
对于链的情况,在距离 L L L 之外的范围,我们可以确定一定都要放点的,所以我们就把这些必要的情况处理出来,并且将向外能延长的最长距离 m a x l e n maxlen maxlen 记录下来。(即可能在链上某一个点放上点,可以覆盖到另外的点) 如果一条链的长度小于 m a x l e n maxlen maxlen ,那么我们这个时候甚至不需要在中心放点去覆盖。
对于环的情况,如果我们要达到上述链的情况,就需要在两边都放点,还不如在中心点放一个点去覆盖,有点不划算。 所以我们也就将必要的位置都先填上(即两边去掉 L L L 之后的长度),并用尽可能平均的剩下的长度去和 m a x l e n maxlen maxlen 和 L L L 比较。
最后看中心点是否被覆盖到了来确定还要不要加点。
代码
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define rep_e(i,u) for(int i=head[u];~i;i=nex[i])
using namespace std;
typedef long long ll;
const int maxn=1005;
const int maxm=maxn*maxn;
int de[maxn],n,m,k,rt,dep[maxn];
int head[maxn],to[maxm],nex[maxm],cnt;
vector<int> Link,Cir;
bool cmp(int a,int b){
return a>b;
}
void add(int u,int v){
to[cnt]=v;nex[cnt]=head[u];
head[u]=cnt++;
}
int Gain_P(int L){
int Ra=-1,flag=0,ret=0,mx=-1;
int H=2*L+1;
for(auto l: Link){
if(l<=L) continue;
int tmp=l-L;
int need=tmp/H;
tmp-=need*H;
if(tmp){
Ra=max(Ra,H-tmp-L-1);
}
}
for(auto l: Link){
if(l<=Ra) continue;
if(l<=L) {
flag=1; continue;
}
int tmp=l-L;
int need=tmp/H;
ret+=need;
tmp-=need*H;
if(tmp){
ret++;
}
int res=tmp+L-(tmp?1:0)*H;
if(res<=Ra) continue;
else flag=1;
}
for(auto l:Cir){
//printf("%d\n",l);
int Le=(l+1)/2;
if(Le<=Ra) continue;
if(Le<=L) {
flag=1; continue;
}
int tmp=l-2*L;
int need=(tmp+H-1)/H;
ret+=need;
int res=l-need*H;
if((res+1)/2<=Ra) continue;
else {
flag=1;
}
}
if(Ra==-1) flag=1;
return ret+flag;
}
int ck(int l){
int P=Gain_P(l);
//printf("l = %d P = %d\n",l,P);
return P<=k;
}
void dfs(int u,int f,int d){
dep[u]=d;
int flag=0;
rep_e(i,u){
int v=to[i];
if(v==f) continue;
if(!dep[v]) dfs(v,u,d+1);
else Cir.push_back(dep[v]-dep[u]);
flag=1;
}
if(!flag) Link.push_back(dep[u]-1);
}
int main(){
memset(head,-1,sizeof(head));
scanf("%d%d%d",&n,&m,&k);
rep(i,1,m){
int x,y;scanf("%d%d",&x,&y);
add(x,y); add(y,x);
de[x]++,de[y]++;
}
if(k==n) return 0*printf("0\n");
rep(i,1,n) if(de[i]>2) rt=i;
if(rt==0){
int L=1,R=m,ans=-1;
while(L<=R){
int mid=L+R>>1;
int M=mid*2+1;
int ne=(n+M-1)/M;
if(ne<=k){ ans=mid; R=mid-1;}
else L=mid+1;
}
return 0*printf("%d\n",ans);
}
dfs(rt,-1,1);
sort(Link.begin(),Link.end(),cmp);
sort(Cir.begin(),Cir.end(),cmp);
int L=1,R=m,ans=m;
while(L<=R){
int mid=(L+R)/2;
if(ck(mid)){
ans=mid; R=mid-1;
}
else L=mid+1;
}
printf("%d\n",ans);
return 0;
}