codeforces 379F-New Year Tree

传送门: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\n",ans);
89     }
90     return 0;
91 }
View Code

 

转载于:https://www.cnblogs.com/Forever-666/p/10660239.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值