題目鏈接: https://codeforces.com/gym/100519/problem/H
題意:
你現在有一個特殊的無向圖,這個圖除了最多某一個點外,其餘的點的度數均爲二,每個點上都有一隻兔子,現在災難要來了,你可以在 個點上打洞,每隻兔子會往最近的洞跑,問你跑的最遠的兔子的最短距離是多少。
做法:
二分距離,重點是在已知當前距離 情況下的判斷。
很明顯我們可以知道距離 是個很特殊的距離,假如我們在中心點放一個點,那麼 範圍內的點都可以被消除,並且我們只需要一個點,比較賺。
對於鏈的情況,在距離 之外的範圍,我們可以確定一定都要放點的,所以我們就把這些必要的情況處理出來,並且將向外能延長的最長距離 記錄下來。(即可能在鏈上某一個點放上點,可以覆蓋到另外的點) 如果一條鏈的長度小於 ,那麼我們這個時候甚至不需要在中心放點去覆蓋。
對於環的情況,如果我們要達到上述鏈的情況,就需要在兩邊都放點,還不如在中心點放一個點去覆蓋,有點不划算。 所以我們也就將必要的位置都先填上(即兩邊去掉 之後的長度),並用儘可能平均的剩下的長度去和 和 比較。
最後看中心點是否被覆蓋到了來確定還要不要加點。
代碼
#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;
}