學習筆記:點分治
阿新 • • 發佈:2021-08-17
【題目】
-
看了上面的題目,想必有很多種演算法解決。
-
\(1\)、列舉不同的兩個點,然後\(dfs\)算出之間的距離,判斷一下就行了。時間複雜度大概是\(O(n^3)\)。
-
\(2\)、選一個節點做樹的根,求出每個點的深度。然後列舉兩個點,求\(lca\),簡單加減一下就行了。時間複雜度大概是\(O(n^2logn)\)。
-
\(3\)、時間複雜度為\(O(nlogn)\)的演算法點分治。
【點分治演算法】
- 點分治要我們每次選擇一個點\(A\),處理有關點\(A\)的路徑,然後將點\(A\)刪除,然後重複以上過程。
第一步:找重心
- 選擇哪個點成為點\(A\)
-
顯然選\(y\)比選\(x\)不優,選\(x\)最多遞迴\(2\)層,選\(y\)最多遞迴\(4\)層。
-
所以,點\(A\)應該是當前子樹的重心(重心所有的子樹大小的最大值小於整個樹大小的一半)。
第二步:求距離
-
有關點\(A\)的路徑無非就兩種情況。
- 點\(A\)為鏈首,點\(A\)的子樹上另外一點為鏈尾
- 點\(A\)其中一個子樹上的點為鏈首,點\(A\)另外一棵子樹上的點為鏈尾,繞過點\(A\)
-
我們對重心的子樹逐一處理\(\Longrightarrow\)就是先處理完一個子樹,再處理另一個子樹。
-
先算出當前子樹第\(i\)
-
當前子樹查詢完了以後,便將\(d_i\)丟進桶裡。
-
切忌一邊查詢一邊將\(d_i\)丟進桶裡
-
處理完所有子樹,便在桶裡查詢\(k\),代表尋找子樹中是否有一點與點\(A\)距離為\(k\)。
第三步:遞迴
- 在\(A\)的子樹進行上述操作。
【程式碼】
#include<bits/stdc++.h> using namespace std; const int INF=2147483647; const int N=10100; int n,m,b[N]; bool ans[N]; struct EDGE { int v,w,nx; }lb[N<<1]; int tot=1,top[N]; void add(int u,int v,int w) { lb[++tot]=(EDGE){v,w,top[u]}; top[u]=tot; } bool vis[N]; int siz[N],masiz[N],q[N],he,ta; void dfs0(int u) { q[++ta]=u,vis[u]=1; siz[u]=1,masiz[u]=0; for(int i=top[u]; i; i=lb[i].nx) { int v=lb[i].v; if(vis[v]) continue; dfs0(v); siz[u]+=siz[v]; masiz[u]=max(masiz[u],siz[v]); } vis[u]=0; } int d[N]; void dfs1(int u) { q[++ta]=u,vis[u]=1; for(int i=top[u]; i; i=lb[i].nx) { int v=lb[i].v,w=lb[i].w; if(vis[v]) continue; d[v]=d[u]+w; dfs1(v); } vis[u]=0; } bool t[10000010]; void calc(int l,int r) { for(int i=l; i<=r; i++) { int u=q[i]; for(int j=1; j<=m; j++) if(b[j]>=d[u])ans[j]|=t[b[j]-d[u]]; } for(int i=l; i<=r; i++) if(d[q[i]]<=1e7)t[d[q[i]]]=1; } void clear() { for(int i=1; i<=ta; i++) if(d[q[i]]<=1e7)t[d[q[i]]]=0; } void solve(int u) { he=1,ta=0,dfs0(u); int mi=INF,s=siz[u]; for(int i=1; i<=ta; i++) { int v=q[i],g=max(masiz[v],s-siz[v]); if(g<mi) mi=g,u=v; } he=1,ta=0; d[u]=0,vis[u]=1; for(int i=top[u]; i; i=lb[i].nx) { int v=lb[i].v,w=lb[i].w; if(vis[v])continue; d[v]=d[u]+w; dfs1(v); calc(he,ta); he=ta+1; } q[++ta]=u; calc(he,ta); clear(); for(int i=top[u]; i; i=lb[i].nx) { int v=lb[i].v; if(vis[v]) continue; solve(v); } } int main() { scanf("%d%d",&n,&m); for(int i=1; i< n; i++) { int x,y,w; scanf("%d%d%d",&x,&y,&w); add(x,y,w),add(y,x,w); } for(int i=1; i<=m; i++)scanf("%d",&b[i]); solve(1); for(int i=1; i<=m; i++) if(ans[i])printf("AYE\n"); else printf("NAY\n"); return 0; }