2014-2015 ACM-ICPC Moscow Subregional H - Holes 思维

题目链接: 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值