题目大意:给出一棵树,树的生成图G上任意两点相连且两点的距离为树上两点的带权距离,求图上最短的哈密顿回路。
坑点:如果一直想从哈密顿回路入手,那你就要GG了,像我一样想出一个错解DP。
然而,只需画几个图,你就会大致猜出一个方法:以树的重心为根,把书分为若干部分且每个部分大小不超过n/2,那么必定有一种方法使得lca全部取得根,所以对于一条边(u,pre[u]),他会被经过2*s[u]次。
然后就可以依次计算最小值了(?????为什么?????)
这里有一个引理:有n个不同颜色的小球,若出现次数最多的颜色的小球能被分隔开,则一定存在一种排列方式使任意相邻小球不同色。证明很简单,由于出现次数最多的小球能被隔开,按BFS方式每次取出一种颜色的球,这样一定会剩下出现次数最多的球,再把这些球插进序列中就好。由于保证了出现次数最多的颜色的小球能被分隔开,所以这个操作一定能成功
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<set> #include<queue> #include<ctime> #define MAXN 200005 #define ll long long #define maxn 15 #define maxs 1000005 #define inf 1e9 #define eps 1e-9 using namespace std; inline char gc() { static char now[1<<16],*S,*T; if (T==S) { T=(S=now)+fread(now,1,1<<16,stdin); if (T==S) return EOF; } return *S++; } inline ll readlong() { ll x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-')f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x*=10; x+=ch-'0'; ch=getchar(); } return x*f; } inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-')f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x*=10; x+=ch-'0'; ch=getchar(); } return x*f; } void putint(long long t) { int ans[40]= {0}; for(; t; t/=10)ans[++ans[0]]=t%10; for(; ans[0]; ans[0]--)putchar('0'+ans[ans[0]]); putchar('\n'); } const int N=1000005; struct node{ int to,nxt; }e[N<<1]; int h[N],cnt; void add(int x,int y){ e[++cnt]=(node){y,h[x]}; h[x]=cnt; e[++cnt]=(node){x,h[y]}; h[y]=cnt; } int sum[N],ans[N]; int tot; int n; void dfs(int x,int fa){ sum[x]=1; for(int i=h[x];i;i=e[i].nxt){ int y=e[i].to; if(y==fa){ continue; } dfs(y,x); sum[x]+=sum[y]; } if(fa){ ans[++tot]=2*min(sum[x],n-sum[x]); } } int main(){ n=read(); for(int i=1;i<n;i++){ int x=read(); int y=read(); add(x,y); } dfs(1,0); sort(ans+1,ans+tot+1); ll A=0; for(int i=1;i<=tot;i++){ A=(A+1ll*i*ans[tot-i+1]); } printf("%lld\n",A); return 0; }