资料在这里
先上两道树的重心。
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; }
#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; }
两次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; }