树的重心、直径

资料在这里

先上两道树的重心。

poj1655

#include<cstdio>
#include<iostream>
#include<cstring>
#define Maxn 40010
using namespace std;

struct edge{
    int to,next;
}p[Maxn];
int vis[Maxn],st[Maxn],son[Maxn],head[Maxn];
int tot,top;
void addedge(int a,int b){
    p[tot].to=b;
    p[tot].next=head[a];
    head[a]=tot++;
    p[tot].to=a;
    p[tot].next=head[b];
    head[b]=tot++;
}
int ans,id,n;
void dfs(int u){
    st[++top]=u;
    while(top){
        int v=st[top],sum=0,bal=0;
        if(!vis[v]){
            vis[v]=1;
            for(int i=head[v];i!=-1;i=p[i].next)
                if(!vis[p[i].to]){
                    st[++top]=p[i].to;
                }
        }
        else{
            for(int i=head[v];i!=-1;i=p[i].next){
                int r=p[i].to;
                if(vis[r]==2){
                    sum+=son[r];
                    bal=max(bal,son[r]);
                }
            }
            son[v]=sum+1;
            bal=max(bal,n-son[v]);
            vis[v]++;
            top--;
            if(bal<ans||bal==ans&&v<id){
                ans=bal,id=v;
            }
        }
    }
}
int main()
{
    int t,a,b;
    cin>>t;
    while(t--){
        cin>>n;
        tot=top=0;
        memset(head,-1,sizeof head);
        memset(vis,0,sizeof vis);
        for(int i=1;i<n;i++){
            scanf("%d%d",&a,&b);
            addedge(a,b);
        }
        ans=1<<30;
        dfs(1);
        printf("%d %d\n",id,ans);
    }
	return 0;
}

poj3107
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define Maxn 100010
using namespace std;

struct edge{
    int to,next;
}p[Maxn];
int tot,num,res,n;
int head[Maxn],ans[Maxn],son[Maxn];
void addedge(int a,int b){
    p[tot].to=b;
    p[tot].next=head[a];
    head[a]=tot++;
    p[tot].to=a;
    p[tot].next=head[b];
    head[b]=tot++;
}
void dfs(int u,int fa){
    int sum=0,bal=0;
    for(int i=head[u];i!=-1;i=p[i].next){
        int v=p[i].to;
        if(v!=fa){
            dfs(v,u);
            sum+=son[v];
            bal=max(bal,son[v]);
        }
    }
    son[u]=sum+1;
    bal=max(bal,n-son[u]);
    if(bal<res){
        res=bal;
        num=0;
        ans[num++]=u;
    }
    else if(bal==res) ans[num++]=u;
}
int main()
{
    int a,b;
    while(cin>>n){
        tot=num=0;
        res=1<<30;
        memset(head,-1,sizeof head);
        for(int i=1;i<n;i++){
            scanf("%d%d",&a,&b);
            addedge(a,b);
        }
        dfs(1,-1);
        sort(ans,ans+num);
        printf("%d",ans[0]);
        for(int i=1;i<num;i++)
            printf(" %d",ans[i]);
        puts("");
    }
	return 0;
}

直径

树形dp法

#include<cstdio>
#include<iostream>
#include<cstring>
#define Maxn 20010
using namespace std;

struct edge{
    int to,v,next;
}p[Maxn];
int max1[Maxn],max2[Maxn],head[Maxn];
int tot,res;
void addedge(int a,int b,int c){
    p[tot].to=b;
    p[tot].v=c;
    p[tot].next=head[a];
    head[a]=tot++;
}
void dfs(int u,int fa){
    for(int i=head[u];i!=-1;i=p[i].next){
        int v=p[i].to;
        if(v!=fa){
            dfs(v,u);
            if(max1[v]+p[i].v>max1[u]){
                max2[u]=max1[u];
                max1[u]=max1[v]+p[i].v;
            }
            else max2[u]=max(max2[u],max1[v]+p[i].v);
        }
    }
    res=max(res,max1[u]+max2[u]);
}
int main()
{
    int n,a,b,c;
    cin>>n;
    memset(head,-1,sizeof head);
    for(int i=1;i<n;i++){
        cin>>a>>b>>c;
        addedge(a,b,c);
        addedge(b,a,c);
    }
    dfs(1,-1);
    cout<<res<<endl;
	return 0;
}

bzoj2282(后台测试数据有问题,故不能保证程序是对的)

两次bfs搜直径,之后搜侧链最大长度,可证明答案必然是直径的某一段,二分长度即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define Maxn 600010
using namespace std;

struct edge{
    int to,v,next;
}p[Maxn];
int tot,top;
int head[Maxn],st[Maxn],mark[Maxn],dist[Maxn],q[Maxn],from[Maxn];
int pre[Maxn];
void addedge(int a,int b,int c){
    p[tot].to=b;
    p[tot].v=c;
    p[tot].next=head[a];
    head[a]=tot++;
}
void bfs(int u){
    memset(dist,-1,sizeof dist);
    int s=0,e=-1;
    q[++e]=u,dist[u]=0;
    while(s<=e){
        int v=q[s++];
        for(int i=head[v];i!=-1;i=p[i].next){
            int r=p[i].to;
            if(dist[r]==-1){
                q[++e]=r;
                if(mark[r]) dist[r]=dist[v];
                else dist[r]=dist[v]+p[i].v;
                from[r]=v;
            }
        }
    }
}
bool check(int mid,int s){
    int t=upper_bound(pre,pre+top,mid)-pre-1;
    t=upper_bound(pre,pre+top,pre[t]+s)-pre-1;
    return pre[top-1]-pre[t]<=mid;
}
int main()
{
    int n,s,a,b,c;
    cin>>n>>s;
    tot=0;
    memset(head,-1,sizeof head);
    for(int i=1;i<n;i++){
        scanf("%d%d%d",&a,&b,&c);
        addedge(a,b,c);
        addedge(b,a,c);
    }
    int da,db;
    bfs(1);
    da=1;
    for(int i=1;i<=n;i++)
        if(dist[i]>dist[da]) da=i;
    bfs(da);
    db=1;
    for(int i=1;i<=n;i++)
        if(dist[i]>dist[db]) db=i;
    top=0;
    while(db!=da){st[top++]=db;db=from[db];}
    st[top++]=da;
    for(int i=0;i<(top-1)/2;i++) swap(st[i],st[top-1-i]);
    for(int i=0;i<top;i++) pre[i]=dist[st[i]];
    int l=0,r=dist[st[top-1]];
    memset(mark,0,sizeof mark);
    for(int i=0;i<top;i++) mark[st[i]]=1;
    bfs(da);
    for(int i=1;i<=n;i++) l=max(l,dist[i]);
    while(l<r){
        int mid=l+r>>1;
        if(check(mid,s)) r=mid;
        else l=mid+1;
    }
    printf("%d\n",l);
    return 0;
}

你可能感兴趣的:(树的重心、直径)