CF842E Nikita and game

CF842E Nikita and game

题意简述:每次加一个点,求加点后树的直径的端点数。

数据范围\(N\le 3\times 10^5\)

Solution

维护直径的"两部",开两个std::vector保存它们。

每次新加一个点,考虑对直径的变化。由于直径的最长性,直径每次只会加\(1\)

我们考虑每次对维护的std::vector进行操作,如果以它为一端的直径长度和之前相等,我们将它加入一个std::vector。如果以它为一端的直径长度大于答案,我们将一个std::vector清空。

我们发现每个点最多只会加入一次,删除数显然小于加入数,所以我们暴力维护就行了。

现在我们只需找到每个点所对应的最长路径。如果我们枚举直径的另一端,每次找到直径的时间复杂度是\(O(N log N)\),总的时间复杂度是\(O(N^2log N)\)

  • 我们考虑直径的性质。所有的直径都是相交的

我们用反证法证明:在一张边权非负的树上,如果有两条不相交的直径,由于树上任意两点一定是连通的,我们交换两条直径的一端,一定能找到一个更大的直径。这与直径的最大性相悖。所以所有的直径都是相交的。

  • 所以两个点集构成的路径上一定有重合的地方,我们称它为关键点

容易发现,在一个点集内的任意两点到关键点的距离相等。

一个点如果想要更新答案,那么他一定加入到了一个点集(不管其他的点有没有清空)。

如果它是一个新的答案,那么直径一定是过关键点的。否则它就不可能是直径(没有与其他直径都相交)。

所以如果这个点能更新答案,那么它到点集内任意一个点的距离都是一样长的,我们只需\(O(log N)\)的用一个点集内第一个点进行转移判断就行了,总的时间复杂度为\(O(Nlog N)\)

代码如下

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N=3e5+10;

int n,f[N][30],d[N],cnt;
vector<ll>S,T;
ll D;

void add(int x,int fa){
    f[x][0]=fa,d[x]=d[fa]+1;
    for(int i=1;(1<<i)<=d[x];i++)
        f[x][i]=f[f[x][i-1]][i-1];
}

int lca(int x,int y){
    if(d[x]<d[y])swap(x,y);
    for(int i=25;i>=0;i--)if(d[f[x][i]]>=d[y])x=f[x][i];
    if(x==y)return x;
    for(int i=25;i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    return f[x][0];
}

ll dist(ll x,ll y){
    return d[x]+d[y]-2*d[lca(x,y)];
}

int main(){
    scanf("%d",&n);
    d[1]=0,cnt=1;
    S.push_back(1);
    while(n--){
        cnt++;
        int x;scanf("%d",&x);add(cnt,x);
        ll dis1=0,dis2=0;
        if(S.size())dis1=max(dis1,dist(cnt,S[0]));
        if(T.size())dis2=max(dis2,dist(cnt,T[0]));
        if(dis1>D||dis2>D){
            D=max(dis1,dis2);
            if(D==dis1){
                for(auto y:T)if(dist(cnt,y)==D)S.push_back(y);
                T.clear();
                T.push_back(cnt);
            }else if(D==dis2){
                for(auto y:S)if(dist(cnt,y)==D)T.push_back(y);
                S.clear();
                S.push_back(cnt);
            }
        }else{
            if(dis1==D)T.push_back(cnt);
            else if(dis2==D)S.push_back(cnt);
        }
        printf("%lld\n",S.size()+T.size());
    }
    return 0;
}
posted @ 2024-10-04 18:36  lichenyu_ac  阅读(13)  评论(0)    收藏  举报