51nod 1576 Tree and permutation(樹的重心+dfn序)
阿新 • • 發佈:2019-03-05
就是 fin 怎麽 add ostream etl ++ nod 每一個 沒有向下取整。
所以就是求所有的點到重心的距離和。
然後加點的話可以離線然後\(dfn序\)維護一下。
乍一看我不會。
先不考慮加點。
先考慮沒有那個除\(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序)