算法学习:点分治
【定義】
【樹的重心】所有子樹的大小都不超過整個樹大小的一半的點
【重心的求取】依次查找每個點,當此點的最大子樹最小時肯定是重心
?
?
?
【解決問題】
處理樹上路徑信息
類似這樣:
一棵樹內n個點,求距離等于k的點對個數
一棵樹內n個點,距離等于質數的點的對數
一棵樹內n個點,是否存在距離等于k的點
.....................................
?
?
【解決思路】
?直接莽,n^3的復雜度,肯定炸
所以我們需要想一種方法,能夠顯著減少其復雜度,于是就用到了分治
選取一個節點作為將這個DAG的根,使其變成一顆樹,
想象一手拿著根,提溜著抖摟一下然后就成了一顆樹,下面有若干顆子樹
?
于是距離符合要求的情況有兩種,
第一種:兩個點都在同一顆子樹中
第二種:兩個點在不同子樹中,但是很明顯他們都經過了根節點
?
所以我們計算出各個點到根節點的距離,然后排序比較一番就能夠在nlogn的時間內求得答案,就能夠解決第二種情況的問題
?
而第一種情況,我們再向下繼續進行找根變成樹的操作,他們兩個也會在不同的子樹中
這樣子,第一種情況就會變成第二種,我們也就只需要考慮如何處理第二種和如何把第一種變成第二種
?
第一種變成第二種的關鍵在于這個根的選取,如果選的根不對很有可能選出來一個鏈,然后就O(n)處理,果斷GG
怎么分治才能使層數盡量小,使兩邊盡量均衡,達到二分的效果的情況下層數更少,能夠縮減至logn,所以我們需要取重心做,
這個根是重心,所以下面的子樹的層數會是最少的
?
?
?
#include<map> #include<cstdio> #include<iostream> #include<algorithm> using namespace std; const int MAXN = 10010; const int MAXM = 20010; const int MAXK = 10000010; struct node {int to;int val;int nt; }edge[MAXM]; int st[MAXN], ask[110],top; int siz[MAXN], f[MAXN], root; int dis[MAXN], a[MAXN], S,cnt,n,m; int ans[MAXK]; bool vis[MAXN]; void add(int x,int y,int z) {top++; edge[top] = { y,z,st[x] }; st[x] = top; } void get_root(int x, int fa) {siz[x] = 1, f[x] = 0;for (int i = st[x]; i; i = edge[i].nt){int to = edge[i].to;if (to == fa || vis[to]) continue;get_root(to, x);f[x] = max(f[x], siz[to]);siz[x] += siz[to];}f[x] = max(f[x], S - siz[x]);if (f[x] < f[root]){root = x;} } void get_dis(int x, int fa,int d) {dis[++cnt] = a[x];for (int i = st[x]; i; i = edge[i].nt){int to = edge[i].to;if (vis[to] || to == fa) continue;a[to] = d + edge[i].val;get_dis(to, x,a[to]);} } void solve(int x,int len,int w) // {cnt = 0;a[x] = len;get_dis(x, 0, len);for(int i=1;i<=cnt;i++)for (int j = 1; j <= cnt; j++){if (i != j && dis[i]+dis[j]<MAXK)ans[dis[i] + dis[j]] += w;} } void divide(int x) {solve(x, 0, 1);vis[x] = 1;for (int i = st[x]; i; i = edge[i].nt){int to = edge[i].to;if (vis[to]) continue;solve(to, edge[i].val, -1);S = siz[x], root = 0, f[0] = n;get_root(to, x);divide(root);} } int main() {scanf("%d%d", &n, &m);for (int i = 1; i <= n-1; i++){int x, y, z;scanf("%d%d%d", &x, &y, &z);add(x, y, z);add(y, x, z);}siz[0] = 0,f[0] = n, root = 0,S=n;get_root(1, 0);divide(root);for (int i = 1; i <= m; i++){int k;scanf("%d", &k);if (ans[k])printf("AYE\n");elseprintf("NAY\n");}return 0; } View Code?
轉載于:https://www.cnblogs.com/rentu/p/11284495.html
總結
- 上一篇: 杭电多校(四)2019.7.31--暑假
- 下一篇: SpringBoot配置属性之DataS