傳送門c++
雖然這題是一道二合一,也不算難,但仍是學到了不少東西啊,\(k\) 級兒子個數的五種求法!!我仍是以爲四種比較好(數組
\(k\) 級兒子個數有五種求法,你知道麼? ——魯迅spa
首先 \(k\) 級祖先很好求,離線的話dfs的時候開個棧就行了。長鏈剖分也能夠但我不會,倍增什麼的就不用說了。code
就是求一個子樹裏爲某一個深度的點的個數嘛,這個明顯能夠dsu on tree啊,開個桶記錄下各類深度的有幾個就行了。get
複雜度:\(O(nlogn)\),應該不能過0_0it
轉化爲dfs序,就是一個區間裏等於某一個數的個數,二維數點弱化版,離線+樹狀數組。模板
複雜度:\(O(nlogn)\)class
給每一個深度開一個vector,按照dfs序把點塞進去,詢問時只要在對應深度的vector裏二分出區間左右端點就行了。統計
這個作法雖然也是 \(O(nlogn)\) ,但它是在線的,很妙啊!!co
這個是模板了吧,用一個簡單的DP統計一下就行了
複雜度 \(O(n)\)
由於我以前其實不會長鏈剖分因此就寫了下代碼……
#include<bits/stdc++.h> #define ll long long #define fr(i,x,y) for(int i=(x);i<=(y);i++) #define rf(i,x,y) for(int i=(x);i>=(y);i--) #define frl(i,x,y) for(int i=(x);i<(y);i++) using namespace std; const int N=1000003; const int M=N<<1; int n,q; int cnt,head[N],Next[M],v[M]; vector<int> id[N]; int qk[N]; void read(int &x){ char ch=getchar();x=0; for(;ch<'0'||ch>'9';ch=getchar()); for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+ch-'0'; } void add(int x,int y){ Next[++cnt]=head[x]; head[x]=cnt; v[cnt]=y; } int st[N],L; int d[N],bc[N],ls[N],*f[N],*now=ls; //vector<int> qry[N]; void predfs(int x,int fa,int dep){ st[dep]=x; //int bc=0; for(auto tmp:id[x]) if (dep>qk[tmp]) id[st[dep-qk[tmp]]].push_back(tmp); id[x].resize(0); for(int i=head[x];i;i=Next[i]){ predfs(v[i],x,dep+1); if (d[v[i]]>d[bc[x]]) bc[x]=v[i]; } d[x]=d[bc[x]]+1; } int ans[N]; void dfs(int x,int fa){ f[x][0]=1; if (bc[x]) f[bc[x]]=f[x]+1,dfs(bc[x],x); for(int i=head[x];i;i=Next[i]){ int tmp=v[i]; if (tmp==bc[x]) continue; f[tmp]=now;now+=d[tmp]; dfs(tmp,x); fr(j,1,d[tmp]) f[x][j]+=f[tmp][j-1]; } for(auto tmp:id[x]) ans[tmp]=f[x][qk[tmp]]-1; } int main(){ read(n);read(q); int x; fr(i,2,n){ read(x); add(x,i); } fr(i,1,q){ read(x);read(qk[i]); id[x].push_back(i); } predfs(1,0,1); f[1]=now;now+=d[1]; fr(i,1,n) for(auto j:id[i]) printf("%d ",j);puts("---"); dfs(1,0); fr(i,1,q) printf("%d ",qk[i]==0?0:ans[i]); return 0; }
這是標算,,想不到……
前面那個樹狀數組未免太大材小用了,由於咱們只是求區間裏等於一個數的個數,並不真的須要樹狀數組所維護的前綴和。
咱們dfs的時候記錄一個 \(cnt_i\) 表示dfs過的裏面深度爲 \(i\) 的有多少個,而後求一個子樹裏深度爲 \(d\) 的個數只要把dfs這個子樹先後的 \(cnt_d\) 減一減就行了。