题意:每次在树上加一个点,问树上有多少个起点存在与另一点的距离=直径长?
标程:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=300005; 4 int dep[N],n,fa[N][22],d1,d2,len; 5 set<int> s1,s2; 6 set<int> ::iterator t; 7 int lca(int x,int y) 8 { 9 if (dep[x]<dep[y]) swap(x,y); 10 int del=dep[x]-dep[y]; 11 for (int i=20;i>=0;i--) 12 if (del&(1<<i)) x=fa[x][i]; 13 if (x==y) return x; 14 for (int i=20;i>=0;i--) 15 if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; 16 return fa[x][0]; 17 } 18 int dis(int x,int y){return dep[x]+dep[y]-2*dep[lca(x,y)];} 19 void init() 20 { 21 for (int j=1;j<=20;j++)//预处理正循环 22 for (int i=2;i<=n+1;i++) 23 fa[i][j]=fa[fa[i][j-1]][j-1]; 24 } 25 int main() 26 { 27 scanf("%d",&n); 28 for (int i=2;i<=n+1;i++) scanf("%d",&fa[i][0]),dep[i]=dep[fa[i][0]]+1; 29 init(); s1.insert(1); 30 for (int i=2;i<=n+1;i++) 31 { 32 d1=(int)s1.size()==0?0:dis(i,*s1.begin()); 33 d2=(int)s2.size()==0?0:dis(i,*s2.begin()); 34 if (max(d1,d2)>len) 35 { 36 if (d1>len) 37 { 38 for (t=s2.begin();t!=s2.end();++t) 39 if (dis(i,*t)==d1) s1.insert(*t); 40 s2.clear(); s2.insert(i); 41 }else { 42 for (t=s1.begin();t!=s1.end();++t) 43 if (dis(i,*t)==d2) s2.insert(*t); 44 s1.clear(); s1.insert(i); 45 } 46 len=max(d1,d2); 47 }else if (max(d1,d2)==len) 48 { 49 if (d1==len) s2.insert(i);else s1.insert(i); 50 } 51 printf("%d\n",(int)s1.size()+(int)s2.size()); 52 } 53 return 0; 54 }
易错点:1.lca_init的时候j应该正着枚举。
2.所有直径一定会相交,直径的中点(中心)一定被经过。
题解:set+性质
固定根,直径由离根最远的点的集合s1以及离s1中点最远的点的集合s2中的点组成。
因此维护两个set(vector?),由新加入的点到s1中任意一点的距离和s2中任意一点的距离来判断该点的去向。(可以证明取集合中的任意一点不会影响距离>=原直径的情况)
如果距离>原直径,那么更新此点为s1或s2中的唯一端点,并用被清空的集合更新另一个集合(可以证明有用的端点集合包含于之前的直径端点集合)。
如果距离=原直径,加入s1或s2。
时间复杂度O(nlogn),似乎并不需要set。