传送门:QAQQAQ
题意:原始有一棵根为1,有三个叶子2,3,4的树。有n个操作,每次可以在一个叶子下面续上两个节点,每次操作完问当前树的直径。
思路:先预处理出树的直径,以及其中一条直径两端的点l,r,对于新加的点,只需计算其与两端的距离(倍增LCA),若大于ans,则更新直径l或r,否则就不变
证明:假设加上点x后直径长为ans+1,因为x仅有一条边连它的父亲f,所以从f出发最远点距离为ans,即没加上点x前f为其中一条直径的端点。
因为各个直径两两相交(否则定可以在上方找到一条把两直径连起来,使直径更长),两直径前后两端不同区间一样长,所以从f出发到l,r定有一个距离为ans,所以这样的更新是没有反例的
补:树的直径dfs方法证明
1 设u为s-t路径上的一点,结论显然成立,否则设搜到的最远点为T(往下走)则
dis(u,T) >dis(u,s) 且 dis(u,T)>dis(u,t) 则最长路不是s-t了,与假设矛盾
2 设u不为s-t路径上的点
首先明确,假如u走到了s-t路径上的一点,那么接下来的路径肯定都在s-t上了,而且终点为s或t,在1中已经证明过了
所以现在又有两种情况了:
1:u走到了s-t路径上的某点,假设为X,最后肯定走到某个端点,假设是t ,则路径总长度为dis(u,X)+dis(X,t)
2:u走到最远点的路径u-T与s-t无交点,则dis(u,T) >dis(u,X)+dis(X,t);显然,如果这个式子成立,
则dis(s,X)+dis(X,u)+dis(u,T)>dis(s,X)+dis(X,t)=dis(s,t) 即dis(s,T)>dis(s,t) 最长路不是s-t矛盾 (摘自https://blog.csdn.net/frankax/article/details/79514778)
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int inf=(int)(2e9); 5 const ll INF=(ll)(5e18); 6 const int N=1000010;//pay attention to the size of the array!!! 7 const int M=30; 8 9 int n,l,r,num,ans; 10 int father[N][30],d[N]; 11 vector<int> v[N]; 12 13 void add_edge(int x,int y) 14 { 15 v[x].push_back(y); 16 father[y][0]=x; 17 for(int i=1;i<M;i++) father[y][i]=father[father[y][i-1]][i-1]; 18 d[y]=d[x]+1; 19 } 20 21 void init() 22 { 23 num=5; 24 memset(father,0,sizeof(father)); 25 for(int i=0;i<M;i++) father[1][i]=1; 26 d[1]=0; d[2]=d[3]=d[4]=1; 27 } 28 29 int LCA(int x,int y) 30 { 31 if(d[x]<d[y]) swap(x,y); 32 int dst=d[x]-d[y]; 33 for(int i=M-1;i>=0;i--) 34 { 35 int tmp=(1<<i); 36 if(tmp<=dst) 37 { 38 dst-=tmp; 39 x=father[x][i]; 40 } 41 } 42 for(int i=M-1;i>=0;i--) 43 { 44 if(father[x][i]==father[y][i]) continue; 45 x=father[x][i]; y=father[y][i]; 46 } 47 return father[x][0]; 48 } 49 50 int dis(int x,int y) 51 { 52 if(x==y) return 0; 53 int f=LCA(x,y); 54 return d[x]+d[y]-d[f]*2; 55 } 56 57 void solve(int x) 58 { 59 if(dis(x,l)>ans) 60 { 61 ans=dis(x,l); 62 r=x;//don't confuse the order of l&r 63 } 64 if(dis(x,r)>ans) 65 { 66 ans=dis(x,r); 67 l=x; 68 } 69 } 70 71 int main() 72 { 73 scanf("%d",&n); 74 init(); 75 for(int i=2;i<=4;i++) 76 { 77 add_edge(1,i); 78 } 79 l=2,r=4,ans=2; 80 while(n--) 81 { 82 int x; 83 scanf("%d",&x); 84 add_edge(x,num); 85 add_edge(x,num+1); 86 solve(num); 87 num+=2; 88 printf("%d ",ans); 89 } 90 return 0; 91 }