[Codeforces]860E Arkady and a Nobody-men
屯一個虛樹的板子,順便總結一下這樣的題型。
Description
給定一棵n個節點的有根樹,在輸入數據通過給出每個節點的父親來表示這棵樹。若某個節點的父親為0,那麽該節點即為根。現在對於每個點,詢問它的每個祖先的所有深度不超過該節點的兒子的數量的總和。
Input
第一行一個整數n。第二行n個整數,表示每個節點的父親pi。
Output
輸出一行n個整數,表示每個節點的答案。
Sample Input
5
2 3 4 5 0
Sample Output
10 6 3 1 0
HINT
1<= n <=5*10^5,0<= pi <= n,保證有且僅有一個pi = 0。
Solution
這道題的思路是顯而易見的。
對於每個節點,它的貢獻是它到根的一條鏈(給它的所有祖先的子樹大小+1)。
每個節點的詢問也是它到根的那一條鏈。
至於深度不超過詢問節點,我們發現,對於每個節點,只有深度小等於它的節點才對它有貢獻,而且是一定有貢獻。
所以我們把所有點按深度排序,一層一層地做。插入一層,詢問一層。
用上樹鏈剖分你就會在O(nlog2n)的時間內T掉該題。(如果你用樹剖過了當我沒說)
然後我們考慮怎麽將它優化。
對於修改鏈的問題,我們通常可以通過逆向思考,將其變為子樹修改,復雜度可以從O(nlog2n)降為O(nlogn)。
詢問很顯然是很容易轉化的,每個點都詢問它的所有祖先,相當於每個點都對它的所有子節點加上答案貢獻。
而對於修改我們則要分析一下:
如圖,我們做到了第5層,計算紅色節點對祖先子樹大小的貢獻。
我們發現兩個節點的貢獻會在他們的lca處合並,那麽這樣一來正好構成一棵以該深度的點為葉節點的虛樹!(即綠色節點)
虛樹上每個節點x到它父親的這條鏈上被紅色節點的貢獻val[x]都是相等的,
因此虛樹上每個節點x對該節點的子節點們的貢獻就是val[x]*(dep[x]-dep[fa[x]])!
虛樹的點數和葉節點數同級,所以總復雜度為O(nlogn)。
然而通過觀察,我們還可以發現每一層的節點答案對下一層是具有遞推關系的。
一個結點的答案可以由它父親的答案加上該節點所在層的所有節點對它的貢獻,這個我們同樣可以用虛樹解決。
搞出虛樹上每個節點被紅色節點的貢獻,然後從虛樹根出發,往下dfs,到底層節點就計算一下答案即可。
如果你用上tarjan求lca和一些騷排序(如果你願意的話)可以把時間復雜度優化到O(n)。(你大可把這句話當成是小C在口胡)
代碼是第二種做法:
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> #define ll long long #define MN 500005 #define MS 20 using namespace std; struct edge{int nex,to;}e[MN]; int dfbg[MN],dfed[MN],dep[MN],hr[MN],q[MN],siz[MN],fa[MS][MN],b[MN],u[MN]; ll t[MN],ans[MN]; vector <int> d[MN]; int dfn,pin,bin,tp,n,rt; inline int read() { int n=0,f=1; char c=getchar(); while (c<‘0‘ || c>‘9‘) {if(c==‘-‘)f=-1; c=getchar();} while (c>=‘0‘ && c<=‘9‘) {n=n*10+c-‘0‘; c=getchar();} return n*f; } inline void ins(int x,int y) {e[++pin]=(edge){hr[x],y}; hr[x]=pin;} void dfs(int x,int depth) { dfbg[x]=++dfn; dep[x]=depth; d[depth].push_back(x); for (register int i=hr[x];i;i=e[i].nex) dfs(e[i].to,depth+1); dfed[x]=dfn; } int lca(int x,int y) { register int i,k; if (dep[x]<dep[y]) swap(x,y); for (k=dep[x]-dep[y],i=0;k;k>>=1,++i) if (k&1) x=fa[i][x]; if (x==y) return x; for (i=MS-1;i>=0;--i) if (fa[i][x]!=fa[i][y]) x=fa[i][x],y=fa[i][y]; return fa[0][x]; } inline void pushs(int x) {while (tp&&dfbg[x]>dfed[q[tp]]) --tp; if (tp) ins(q[tp],x); q[++tp]=x;} inline int lowbit(int x) {return x&-x;} inline void getadd(int x,int z) {for (;x<=n;x+=lowbit(x)) t[x]+=z;} inline ll getsum(int x) {ll lt=0; for (;x;x-=lowbit(x)) lt+=t[x]; return lt;} void dp(int x,int fat) { siz[x]=u[x]; for (register int i=hr[x];i;i=e[i].nex) dp(e[i].to,x),siz[x]+=siz[e[i].to]; getadd(dfbg[x]+1, 1LL*siz[x]*(dep[x]-dep[fat])); getadd(dfed[x]+1,-1LL*siz[x]*(dep[x]-dep[fat])); } bool cmp(int x,int y) {return dfbg[x]<dfbg[y];} int main() { register int i,j; n=read(); bin=0; for (i=1;i<=n;++i) ins(fa[0][i]=read(),i),b[++bin]=i; for (i=1;i<=n;++i) if (!fa[0][i]) rt=i; for (i=1;i<MS;++i) for (j=1;j<=n;++j) fa[i][j]=fa[i-1][fa[i-1][j]]; dfs(rt,1); for (i=2;d[i].size();++i) { for (j=1;j<=bin;++j) hr[b[j]]=u[b[j]]=0; tp=pin=bin=0; for (j=0;j<d[i].size();++j) ++u[b[++bin]=fa[0][d[i][j]]]; bin=unique(b+1,b+bin+1)-b-1; for (j=1;j<bin;++j) b[bin+j]=lca(b[j],b[j+1]); bin=bin*2-1; sort(b+1,b+bin+1,cmp); bin=unique(b+1,b+bin+1)-b-1; for (j=1;j<=bin;++j) pushs(b[j]); dp(q[1],0); for (j=0;j<d[i].size();++j) ans[d[i][j]]=getsum(dfbg[d[i][j]]); } for (i=1;i<=n;++i) printf("%I64d ",ans[i]); }
Last Word
感覺全程打得最難受的是倍增求lca,小C一直覺得自己的lca寫得奇醜無比。
[Codeforces]860E Arkady and a Nobody-men