例題:(板子)
洛谷板子題p3806
洛谷第二個板子題p4178
點分治、感覺還看的懂。
經典問題:找乙個樹上距離為k的有多少對點。
怎麼找呢? 分治思想:遞迴
也就是找乙個合適的根然後把問題分為跨過這個根的兩個點 和 不過這個根的距離也就是在乙個子樹上的兩個點和不在同乙個子樹上的兩個點。處理的時候只處理不過這個根的距離(在同乙個子樹上的)有沒有為k的。因為不過這個根的情況在以後會處理到的。然後刪掉這個根對他的子樹進行上述操作。
找根的時候如果按題上給的根做可能會超時;最壞的時候,對於1->2->3->4->5這種情況時間複雜度是n^2,但是理想的是n*logn由於用哪個節點當根對結果沒有影響,所以如果自己重新找個根可能讓時間複雜度低一點對於上述情況就是找3這個節點當根複雜度最低,也就是這個鏈的中點。但是對於樹就沒有中點這個概念了。
於是有個東西叫重心:就是找乙個節點,讓這個節點的最大子樹最小這個點就叫重心。每次找他的重心當根就可以了。
找重心怎麼找?也就是一遍dfs處理出以這個節點為根的子樹的大小。再用個陣列記錄這個節點所有子樹中最大的子樹大小,然後找個最小的。
分治遞迴的時候找距離怎麼找?也是dfs 找目前節點到根節點的距離記錄在temp陣列中。然後進行自己要做的處理。
第一道水題,板子題:
洛谷板子題
#include
#include
#include
#include
using
namespace std;
struct node
;const
int maxn =
1e5+5;
vector vv[maxn]
;//存圖
int root;
//重心 就是 樹中以該節點為根的最大子樹最小
int num[maxn]
;//標記以他為根的樹的點的個數
int vis[maxn]
;//標記是否刪除
int son[maxn]
;//代表以它為根的最大的子樹
int sum;
//因為 以當前點為根節點時 他的父節點也是他的乙個子節點 當前根dfs時父節點的子樹大小=總的節點個數減去他的子節點的子樹節點個數 sum用來存總的個數
void
dfs(
int x,
int fa)
//這個dfs部分感覺跟樹鏈刨分的 那個dfs差不多
dfs(v,x)
; num[x]
+=num[v];if
(num[v]
>son[x]
)//son[x]存的是x的子樹中最大的子樹的節點個數
son[x]
=num[v];}
if(sum-num[x]
>son[x]
)//他dfs時的父節點作為他的子節點的子樹大小
if(root==-1
||son[root]
>son[x]
)//root應為最大字數最小的那個節點
root=x;
}int q[maxn]
;//詢問
int kk;
//kk個詢問
bool ans[maxn]
;//詢問的答案
int cnt;
//用來計數 得到距離時 計數
int dis[maxn]
;//存根節點到當前節點的距離
bool judge[
10000007];
//用來判斷 該距離是否存在
int temp[maxn]
;//臨時用來存距離
void
getdis
(int x,
int fa)
//這個也是遞迴
}void
solve
(int x)}}
for(
int j=
0;j)//佇列好像就這下面用了一下 可是有什麼用呢 看下面 有用有用!!!
}while
(!qq.
empty()
)//奧對 有用 有用 只是用來存一下各個子樹裡temp陣列的值 以便於 把他們刪除 變為不存在
}void
fenzhi
(int x)
//分治 基本就變一變solve???我猜的
}int
main()
for(
int i=
0;i) root=-1
; sum=n;
dfs(1,
-1);
//得到根節點
dfs(root,-1
);// 再跑一遍是為了得到以root為根的子樹的大小
//printf("%d\n",root);
fenzhi
(root)
;//分治找距離
for(
int i=
0;i)}
第二道:
洛谷第二個板子題
#include
#include
#include
#include
using
namespace std;
const
int maxn =
40006
;struct node
;int m;
vector vv[maxn]
;int summ[
200005];
intlowbit
(int x)
void
add(
int x,
int num)
}int
query
(int x)
return ans;
}int num[maxn]
;int son[maxn]
;int sum,root;
int vis[maxn]
;void
dfs(
int x,
int fa)
if(sum-num[x]
>son[x]
) son[x]
=sum=num[x];if
(root==-1
||son[root]
>son[x]
) root=x;
}int dis[maxn]
;int temp[maxn]
;int cnt=0;
int ans=0;
void
getdis
(int x,
int fa)
}void
solve
(int x)
for(
int j=
0;j}while
(!qq.
empty()
)}void
fenzhi
(int x)
}int
main()
scanf
("%d"
,&m)
; root=-1
; sum=n;
dfs(1,
-1);
dfs(root,-1
);//printf("%d\n",root);
fenzhi
(root)
;printf
("%d\n"
,ans)
;}
然後還有幾道可以去練:
洛谷p2634
洛谷p2664
洛谷p4149
cf161d
在b站上學的,感謝
點分治 動態點分治
實在拖得太久了。先扔掉資料 分治的核心是盡量把乙個整體分成接近的兩個部分,這樣遞迴處理可以讓複雜度從n 變成nlogn。兩個問題,如何區分和如何算答案。對於第乙個問題,重心,然後就是找重心的方法,兩個dfs,對於第二個問題,對於每個重心算當前塊中每個點到重心的答案,然後由重心分開的塊要把多餘的資訊去...
點分治與動態點分治
點分治一般是用於解決樹上路徑問題。樹的重心 把重心這個點割掉後,使所形成的最大的聯通塊大小最小的點。可以證明重心子樹的大小最大不會超過 n over 2 重心可以通過 dfs 一遍求出。maxsiz x 表示割掉點x後所形成的的最大的聯通塊的大小 void dfs int x,int fa max ...
點分治(樹分治)
將原問題分解成若干相同形式,相互獨立的子問題,各個擊破 一般用來解決有關樹上路徑的統計和詢問 p4178 tree 給定一棵 n 個節點的樹,每條邊有邊權,求出樹上兩點距離小於等於 k 的點對數量。暴力做法 o n2 點分治做法 選擇乙個點作為分治中心,令其為rt做dfs。對於一條路徑path u,...