https://codeforces.com/contest/842/problem/E
关键性质:多条直径点的交集必然非空且一定为连续一段
也就是说:必然可以划分成两个集合,使得两个集合各选一个点上的路径必然为一条直径
然后对于此题求端点,我们就可以维护两个集合,然后就行了
至于这题有什么启发,我现在还没想懂
#include<bits/stdc++.h>
using namespace std;
//#define int long long
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<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
//mt19937 rand(time(0));
//mt19937_64 rand(time(0));
//srand(time(0));
#define N 300010
//#define M
//#define mo
int n, m, i, j, k, T;
int u, d1, d2, mxd, dia;
int dep[N], f[N][22];
vector<int>v1, v2;
int lca(int u, int v) {
if(u==v) return u;
if(dep[u]<dep[v]) swap(u, v);
for(int k=20; k>=0; --k)
if(dep[f[u][k]]>=dep[v]) u=f[u][k];
// printf("%d %d %d\n", u, v, f[u][0]);
if(u==v) return u;
for(int k=20; k>=0; --k)
if(f[u][k]!=f[v][k]) u=f[u][k], v=f[v][k];
return f[u][0];
}
int dis(int x, int y) {
int z=lca(x, y);
// printf("dis(%d %d)[%d]=%d\n", x, y, z, dep[x]+dep[y]-2*dep[z]);
return dep[x]+dep[y]-2*dep[z];
}
signed main()
{
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
// T=read();
// while(T--) {
//
// }
n=read(); dep[1]=1;
for(i=2; i<=n+1; ++i) {
u=read(); f[i][0]=u; dep[i]=dep[u]+1;
// printf("f[%lld][0]=%lld\n", i, f[i][0]) ;
for(k=1; k<=20; ++k) f[i][k]=f[f[i][k-1]][k-1];
if(i==2) {
v1.pb(1); v2.pb(2); dia=1;
printf("%lld\n", v1.size()+v2.size());
continue;
}
d1=dis(v1[0], i); d2=dis(v2[0], i); mxd=max(d1, d2);
if(mxd>dia) {
if(d1>dia) {
for(auto j : v2) if(dis(j, i)==mxd) v1.pb(j);
v2.clear(), v2.pb(i);
}
else {
for(auto j : v1) if(dis(j, i)==mxd) v2.pb(j);
v1.clear(), v1.pb(i);
}
dia=mxd;
}
else if(mxd==dia) {
if(d1==dia) v2.pb(i);
else v1.pb(i);
}
printf("%lld\n", v1.size()+v2.size());
// printf("dia : %lld || ", dia);
// for(auto i : v1) printf("%lld ", i); printf(" | ");
// for(auto i : v2) printf("%lld ", i); printf("\n");
}
return 0;
}