传送门
扫了一眼题解,发现没有一个严谨证明的,那么我就来证明一波吧。
首先,我们看一看如何求树的直径:
1.随便定一个根节点,第一遍bfs求出树中深度最深的节点,记为u。
2.以u为根节点,第二遍bfs求出树中深度最深的点v
3.树的直径的端点即为u,v
类比到此题:
我们把政党p中的牛最深的记为maxp
发现:p政党最长的链一定是某个p政党的牛和maxp构成的(类似于树的直径)
考虑如何证明这个结论,采用反证法(自己画一个图比较好理解):
声明:为了简化证明,我们记ab为树上ab两点的最短路径,dep(a)为节点a在树中的深度。
设某个p政党的牛a和另一个深度小于$\max _p 的牛b$构成了最长的链。
首先,我们求出lca(a,b),设为c,求出lca(a,maxp),设为c′。
我们可以知道,c′,c有直接的祖先关系,也有可能c=c′,简单来说,就是在同一条到根节点的链上。(这一点参见虚树的证明)
考虑两种情况:
1.c′的祖先为c(如图),那我们有c′maxp+cc′+dep(c)=dep(maxp),且cb+dep(c)=dep(b),
由dep(b)<dep(maxp),两式相减,发现dep(c)抵消,我们有cb<c′maxp+cc′...∗
发现ab=ac′+c′c+cb,且amaxp=ac′+c′maxp,将∗式代入,我们有ab=ac′+c′c+cb<ac′+c′c+c′maxp+c′c=amaxp+cc′×2
由于c′的祖先为c,所以cc′>0,所以ab<amaxp,假设错误。
2.c的祖先为c,证明方法差不多,不再赘述。
所以,我们求一波LCA,然后预处理出maxp,最后暴力扫过一遍所有的牛,求出答案。
时间复杂度O(nlogn)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| #include <bits/stdc++.h> #define MAXN 200005 #define MAXM 25 using namespace std; 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=(x<<3)+(x<<1)+(ch^'0'); ch=getchar(); } return x*f; } vector<int>G[MAXN]; inline void AddEdge(int u,int v){ G[u].push_back(v); } int anc[MAXN][MAXM],dep[MAXN],n,m; void dfs(int u,int father){ anc[u][0]=father; for (register int i=1;i<MAXM;++i){ anc[u][i]=anc[anc[u][i-1]][i-1]; } for (register int i=0;i<G[u].size();++i){ int v=G[u][i]; if (v!=father){ dep[v]=dep[u]+1; dfs(v,u); } } } inline int LCA(int u,int v){ if (dep[u]<dep[v]) swap(u,v); for (register int i=MAXM-1;i>=0;--i){ if (dep[anc[u][i]]>=dep[v]){ u=anc[u][i]; } } if (u==v) return u; for (register int i=MAXM-1;i>=0;--i){ if (anc[u][i]!=anc[v][i]){ u=anc[u][i]; v=anc[v][i]; } } return anc[u][0]; } inline void Init(int root){ memset(dep,0,sizeof(dep)); dfs(root,0); } int val[MAXN],maxu[MAXN],maxdep[MAXN],ans[MAXN]; inline int Dis(int u,int v){ return dep[u]+dep[v]-2*dep[LCA(u,v)]; } int main(){ int n=read(),k=read(); int root; for (register int i=1;i<=n;++i){ int a=read(),p=read(); val[i]=a; AddEdge(i,p),AddEdge(p,i); if (!p) root=i; } Init(root); for (register int i=1;i<=n;++i){ if (dep[maxu[val[i]]]<dep[i]) maxu[val[i]]=i; } for (register int i=1;i<=n;++i){ ans[val[i]]=max(ans[val[i]],Dis(maxu[val[i]],i)); } for (register int i=1;i<=k;++i){ printf("%d\n",ans[i]); } }
|