ZOJ 3820 Building Fire Stations 树的最长链性质(拓展)

题意:

给你一个有n个点的树,n是2个点以上,从中找出两个点作为“消防点”,每个点距离这两个点都有个距离,取短的那个距离叫做“消防距离”,问怎样设置两个点,所有点“消防距离”的最大值最小。


树是有最长链的,最长链上的中点距离其他点的距离最大值是最小的。但是有两个点可以加啊,怎么办?就考虑把树劈开,从最长链的中点处劈开,对于两半各取最长链的中点。对于劈开有歧义的就多找几个点劈几次取最优值。


代码:

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 222222;
struct Edge{
    int u,v,nxt;
    Edge(int u=0,int v=0,int nxt=0):u(u),v(v),nxt(nxt){}
}edge[maxn<<1];
int head[maxn],tot;
void init(int n){
    tot=0;
    for(int i=0;i<=n;i++)
        head[i]=-1;
}
void add(int u,int v){
    edge[tot]=Edge(u,v,head[u]);
    head[u]=tot++;
    edge[tot]=Edge(v,u,head[v]);
    head[v]=tot++;
}
int q[maxn],vis[maxn],pre[maxn],path[maxn];
void clrvis(int n,int u=0){
    for(int i=0;i<=n;i++)
        vis[i] = 0;
    vis[u]=1;
}
int bfs(int u){
    int tail=0,v,farestv=u;
    q[tail++]=u;
    pre[u]=-1;
    vis[u]=1;
    for(int i=0;i<tail;i++){
        u=q[i];
        for(int e=head[u];~e;e=edge[e].nxt){
            v=edge[e].v;
            if(vis[v]) continue;
            vis[v]=vis[u]+1;
            pre[v]=u;
            if(vis[farestv]<vis[v]) farestv=v;
            q[tail++]=v;
        }
    }
    return farestv;
}
int getPath(int t){
    int n=0;
    path[++n]=t;
    while(~pre[t]){
        path[++n]=pre[t];
        t=pre[t];
    }
    return n;
}
void getans(int n){
    int s,t,pathn,ansdis,ansu,ansv;
//    Ans centerAns = cutAndGetAns(n,n+1);
    clrvis(n,n+1);
    s=bfs(1);
    clrvis(n,n+1);
    t=bfs(s);
    pathn = getPath(t);

    if(pathn&1){/**odd number points in d*/
    /**
        u-center|v
    */
        int center = path[pathn/2+1],u=path[pathn/2],v=path[pathn/2+2];
        clrvis(n,v);
        s=bfs(u);
        clrvis(n,v);
        t=bfs(s);

        int un = getPath(t);
        ansu = path[un/2+1];
        ansdis = un/2;

        clrvis(n,center);
        s=bfs(v);
        clrvis(n,center);
        t=bfs(s);

        int vn = getPath(t);
        ansv = path[vn/2+1];
        ansdis = max(ansdis,vn/2);
    /**
    u|center-v
    */
        clrvis(n,u);
        s=bfs(v);
        clrvis(n,u);
        t=bfs(s);

        un = getPath(t);
        int ansu2 = path[un/2+1];
        int ansdis2 = un/2;

        clrvis(n,center);
        s=bfs(u);
        clrvis(n,center);
        t=bfs(s);

        vn = getPath(t);
        int ansv2 = path[vn/2+1];
        ansdis2 = max(ansdis2,vn/2);
    /**
    get the optional answer
    */
        if(ansdis>ansdis2){
            ansu = ansu2;
            ansv = ansv2;
        }
    }else{/**even number points in d*/
        int u=path[pathn/2],v=path[pathn/2+1];
        clrvis(n,v);
        s=bfs(u);
        clrvis(n,v);
        t=bfs(s);

        int un = getPath(t);
        ansu = path[un/2+1];
        ansdis = un/2;

        clrvis(n,u);
        s=bfs(v);
        clrvis(n,u);
        t=bfs(s);

        int vn = getPath(t);
        ansv = path[vn/2+1];
        ansdis = max(ansdis,vn/2);
    }

    printf("%d %d %d\n",ansdis,ansu,ansv);
}
int main(){
//    freopen("data.in","r",stdin);
    int T,n,u,v;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        init(n);
        for(int i=1;i<n;i++){
            scanf("%d%d",&u,&v);
            add(u,v);
        }
        getans(n);
    }
    return 0;
}

spj!

输入:

4


4
1 2
1 3
1 4


20
1 2
2 3
2 7
3 4
3 14
4 5
4 19
5 6
7 8
7 9
9 10
10 11
10 12
12 13
14 15
15 16
15 17
17 18
19 20


7
1 2
2 3
3 4
4 5
5 6
6 7


5
1 2
2 3
3 4
4 5

输出:

1 1 4
4 14 10
2 2 6
1 2 5


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值