點分治本質與模板
阿新 • • 發佈:2021-06-15
點分治
PS:零基礎可以學習模板題的第一篇題解,此隨筆僅做總結
本質:
利用基礎的分治思想,類似線段樹或者歸併,只不過是轉換到樹上。每次需要在當前子樹中找到一個點,這個點可以是重心,或者是滿足"每個子樹大小的最大值\(\leq tot/2\)"的某個點),選取這個點遞迴這個點的子樹,可以保證遞迴\(logn\)層。
隨後在每一層分類討論如何維護每個子樹歸併的答案貢獻(分治的主要思想),要用什麼樣的複雜度的演算法。如果每層用\(O(n)\)的做法,總複雜度就是\(O(nlogn)\)的。每層用\(O(nlogn)\)的做法,總複雜度就是\(O(nlog^2n)\)的。
點分治給我的感覺其實就是個思想,類似啟發式合併。
需要注意的點:
1.要標記一下重心被使用過,以免其餘子樹做操作的時候重複計算之前的重心。
2.注意討論輔助陣列是否在遞迴子樹前是否需要清0。
模板題目:P3806 【模板】點分治1
Code:
#include<bits/stdc++.h> using namespace std; int n,m,k; const int N=1e4+10; struct node{ int to; int next; int dis; }edge[N<<1]; int head[N],num_edge,u,v,_d,base; bool st[N]; bool vis[100000007]; void edge_add(int from,int to,int dis){ edge[++num_edge].next=head[from]; edge[num_edge].to=to; edge[num_edge].dis=dis; head[from]=num_edge; } int get_size(int u,int fa){ if(st[u])return 0; int res=1; for(int i=head[u];i;i=edge[i].next){ int to=edge[i].to; if(to==fa)continue; res+=get_size(to,u); } return res; } #define debug(x) cout<<#x<<" :"<<x<<endl const int M=200; struct node1{ int val; int id; }ask[M]; int ans[M]; int p[N]; int q[N]; int conp=0; int conq=0; int get_wc(int u,int fa,int tot,int &wc){ if(st[u])return 0; int mx=0; int res=1; for(int i=head[u];i;i=edge[i].next){ int to=edge[i].to; if(to==fa)continue; int val=get_wc(to,u,tot,wc); mx=max(mx,val); res+=val; } mx=max(mx,tot-res); if(mx<=tot/2){ wc=u; } return res; } void get_dist(int u,int fa,int dist,int &con){ if(st[u])return ; p[++con]=dist; for(int i=head[u];i;i=edge[i].next){ int to=edge[i].to; if(to==fa)continue; get_dist(to,u,dist+edge[i].dis,con); } } void cal(int u){ if(st[u])return ; get_wc(u,-1,get_size(u,-1),u); st[u]=true; //兩點在一個子樹中 遞迴做 //一點在重心 一點在子樹 //兩點在不同子樹中 conq=0; for(int i=head[u];i;i=edge[i].next){ int to=edge[i].to; get_dist(to,u,edge[i].dis,conp); //列舉一個點在當前子樹,另一個點是從前面所有子樹的內容轉移過來的 for(int j=1;j<=conp;++j){ for(int h=1;h<=m;++h){ if(ask[h].val>=p[j]&&(!ans[ask[h].id])){ if(vis[ask[h].val-p[j]]){ ans[ask[h].id]=1; } } } } for(int j=1;j<=conp;++j){//一個點在重心 一個點在該子樹 vis[p[j]]=1; q[++conq]=p[j]; } conp=0; } for(int i=1;i<=conq;++i){ vis[q[i]]=false;//重置 } for(int i=head[u];i;i=edge[i].next)cal(edge[i].to); } int main(){ scanf("%d %d",&n,&m); for(int i=1;i<=n-1;++i){ int u,v,w; scanf("%d%d%d",&u,&v,&w); edge_add(u,v,w); edge_add(v,u,w); } vis[0]=true; for(int i=1;i<=m;++i){ scanf("%d",&ask[i].val); ask[i].id=i; } cal(1); for(int i=1;i<=m;++i){ if(ans[ask[i].id])puts("AYE"); else puts("NAY"); } return 0; }