1. 程式人生 > >51nod 1576 Tree and permutation(樹的重心+dfn序)

51nod 1576 Tree and permutation(樹的重心+dfn序)

就是 fin 怎麽 add ostream etl ++ nod 每一個

乍一看我不會。
先不考慮加點。
先考慮沒有那個除\(2\)
考慮每一條邊的貢獻,假設某一條邊把這棵樹分成大小為x,y的兩個部分。
那麽這條邊最多可以被使用\(min(x,y)*2\)次(因為有進有出),即貢獻最大為\(min(x,y)*2*\)這條邊的權值。
那麽能不能讓每一條邊的被使用達到最大呢?
顯然可以。
那怎麽快速算這個東西呢?不可能每加一個點就dfs一遍吧。那樣就\(T\)飛了。
實際上這個東西就是每個點到樹的重心的距離\(*2\)
為什麽?因為滿足以樹的重心為根每一個子樹大小\(<\)總共的節點數。每一棵子樹內所有點都要向子樹外也就是根(重心)連邊。
然後發現這個除\(2\)

沒有向下取整。
所以就是求所有的點到重心的距離和。
然後加點的話可以離線然後\(dfn序\)維護一下。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define ls now<<1
#define rs now<<1|1
const int N=101000;
int cnt,head[N];
struct edge{
    int to,nxt;
}e[N*2];
void add_edge(int u,int v){
    cnt++;
    e[cnt].nxt=head[u];
    e[cnt].to=v;
    head[u]=cnt;
}
int size[N],fa[N][22],dep[N],dfn[N],tot;
void dfs(int u,int f){
    size[u]=1;
    dfn[u]=++tot;
    fa[u][0]=f;dep[u]=dep[f]+1;
    for(int i=1;i<=20;i++)fa[u][i]=fa[fa[u][i-1]][i-1];
    int maxson=-1;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(v==f)continue;
        dfs(v,u);
        size[u]+=size[v];
    }
}
int getlca(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    for(int i=20;i>=0;i--)
        if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
    if(x==y)return x;
    for(int i=20;i>=0;i--)
        if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
int sum[N*4];
void update(int now){
    sum[now]=sum[ls]+sum[rs];
}
void build(int l,int r,int now){
    if(l==r){
        if(l==1)sum[l]=1;
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,ls);
    build(mid+1,r,rs);
    update(now);
}
void add(int l,int r,int x,int now){
    if(l==r){
        sum[now]=1;
        return;
    }
    int mid=(l+r)>>1;
    if(x>mid)add(mid+1,r,x,rs);
    else add(l,mid,x,ls);
    update(now);
}
int check(int l,int r,int L,int R,int now){
    if(l==L&&r==R)return sum[now];
    int mid=(l+r)>>1;
    if(L>mid)return check(mid+1,r,L,R,rs);
    else if(R<=mid)return check(l,mid,L,R,ls);
    else return check(l,mid,L,mid,ls)+check(mid+1,r,mid+1,R,rs);
}
int up(int x,int y){
    for(int i=20;i>=0;i--)
        if(dep[fa[x][i]]>dep[y])x=fa[x][i];
    return x;
}
int read(){
    int sum=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    return sum*f;
}
int n,root;
long long ans;
int main(){
    n=read();n++;
    root=1;ans=0;
    for(int i=2;i<=n;i++){
        int v=read();
        add_edge(i,v);add_edge(v,i);
    }
    dfs(1,0);
    build(1,n,1);
    for(int i=2;i<=n;i++){
        add(1,n,dfn[i],1);
        int lca=getlca(root,i);
        ans+=(long long)(dep[root]+dep[i]-2ll*dep[lca]);
        if(lca==root){
            int x=up(i,root);
            int sizex=check(1,n,dfn[x],dfn[x]+size[x]-1,1);
            if(sizex>i-sizex)root=x,ans+=(long long)(i-sizex*2ll);
        }
        else{
            int x=fa[root][0];
            int sizex=check(1,n,dfn[root],dfn[root]+size[root]-1,1);
            if(i-sizex>sizex)root=x,ans+=(long long)(sizex*2ll-i);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

51nod 1576 Tree and permutation(樹的重心+dfn序)