CodeForces - 208E Blood Cousins(树上倍增+二分/树上启发式合并)
                                                            生活随笔
收集整理的這篇文章主要介紹了
                                CodeForces - 208E Blood Cousins(树上倍增+二分/树上启发式合并)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.                        
                                題目鏈接:點擊查看
題目大意:給出n棵樹,再給出m個詢問,每次詢問給出兩個整數u和k,先假設u在k層之上的祖先是p,問與u在同一層深度,并且公共祖先都是p的節點有多少個
題目分析:因為先要求出u在第k層之上的祖先,我們可以利用樹上倍增很簡單的求出,但接下來的操作我是沒想到的,雖然鯤學長已經給了提示需要二分,我也看出了直接dfs暴力找肯定會T掉的,但沒設計出合適的二分來處理這個題,憋不住去網上看了題解后一下子就豁然開朗了,大概就是先用dfs序預處理一下,然后每一層開一個vector,用來儲存每個節點在dfs序后的編號,因為dfs序的緣故,我們在求出u在第k層之上的祖先p后,就可以知道p的子樹中的所有編號肯定都在[L[p],R[p]]之間了,那么我們只需要對于deep[u]這一層的向量進行二分,求出有多少個節點位于這個范圍內就是答案了
實現簡單但思路不好想,挺好的一個題目
2020. 1.14更新:
學了一波樹上啟發式合并,發現對于這個題目而言,要求以每個結點為根的子樹的狀態,不妨直接用啟發式合并搞一波,因為給出的數據是針對于子節點和相對深度,我們可以利用樹上倍增預處理出祖先節點和絕對深度,如此一來就是樹上啟發式合并的模板題了
代碼:
二分:
#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=1e5+100;vector<int>node[N];vector<int>d[N],root;int n,limit,max_deep;int cnt=0;int L[N],R[N];int dp[N][20],deep[N];void dfs(int u,int fa,int dep)//dfs序+樹上倍增 {max_deep=max(max_deep,dep);L[u]=++cnt;d[dep].push_back(cnt);deep[u]=dep;dp[u][0]=fa;for(int i=1;i<=limit;i++)dp[u][i]=dp[dp[u][i-1]][i-1];for(int i=0;i<node[u].size();i++){int v=node[u][i];if(v==fa)continue;dfs(v,u,dep+1);}R[u]=cnt; }int solve(int u,int p) {int h=deep[u];for(int i=0;i<=limit;i++)//先跳到祖先p的位置if((p>>i)&1)u=dp[u][i];if(u==0)//若跳到0節點了,說明祖先不存在,直接返回0return 0;int a=L[u],b=R[u];//查找deep[u]層在[a,b]區間內有多少個節點int l=lower_bound(d[h].begin(),d[h].end(),a)-d[h].begin();int r=upper_bound(d[h].begin(),d[h].end(),b)-d[h].begin()-1;return r-l; }int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);scanf("%d",&n);limit=log2(n)+1;for(int i=1;i<=n;i++){int num;scanf("%d",&num);if(num)node[num].push_back(i);elseroot.push_back(i);}for(int i=0;i<root.size();i++)dfs(root[i],0,0);for(int i=0;i<=max_deep;i++)sort(d[i].begin(),d[i].end());int w;scanf("%d",&w);while(w--){int a,b;scanf("%d%d",&a,&b);printf("%d ",solve(a,b));}return 0; }樹上啟發式合并:
#include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cstring> #include<algorithm> #include<stack> #include<queue> #include<map> #include<set> #include<sstream> #include<unordered_map> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=1e5+100;int deep[N],son[N],num[N],dp[N][20],ans[N],cnt[N],limit;bool vis[N];vector<int>node[N];vector<pair<int,int>>Q[N];//<id,deep>void dfs_son(int u,int dep) {son[u]=-1;num[u]=1;deep[u]=dep;for(int i=1;i<=limit;i++)dp[u][i]=dp[dp[u][i-1]][i-1];for(auto v:node[u]){dfs_son(v,dep+1);num[u]+=num[v];if(son[u]==-1||num[son[u]]<num[v])son[u]=v;} }void cal(int u,int val) {cnt[deep[u]]+=val;for(auto v:node[u]){if(vis[v])continue;cal(v,val);} }void dfs(int u,int keep) {for(auto v:node[u]){if(v==son[u])continue;dfs(v,0);}if(son[u]!=-1){dfs(son[u],1);vis[son[u]]=true;}cal(u,1);for(auto i:Q[u]){int id=i.first;int d=i.second;ans[id]=cnt[d];}if(son[u]!=-1)vis[son[u]]=false;if(!keep)cal(u,-1); }int get_Ancestor(int x,int d) {for(int i=0;i<=limit;i++)if((d>>i)&1)x=dp[x][i];return x; }int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);int n,m;scanf("%d",&n);limit=log2(n)+1;for(int i=1;i<=n;i++){int fa;scanf("%d",&fa);node[fa].push_back(i);dp[i][0]=fa;}dfs_son(0,0);scanf("%d",&m);for(int i=1;i<=m;i++){int u,d;scanf("%d%d",&u,&d);if(deep[u]<=d)ans[i]=1;else{int fa=get_Ancestor(u,d);Q[fa].push_back(make_pair(i,deep[u]));}}dfs(0,1);for(int i=1;i<=m;i++)printf("%d ",ans[i]-1);return 0; }?
總結
以上是生活随笔為你收集整理的CodeForces - 208E Blood Cousins(树上倍增+二分/树上启发式合并)的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: SPOJ - QTREE2 Query
- 下一篇: 计蒜客 - Distance on th
