分治入門 樹分治

2021-08-14 19:56:04 字數 1783 閱讀 4606

分治思想:劃分子問題,解決子問題,合併子問題

題目:poj1741

題意:給定一棵含有n個節點的無向帶權樹,滿足距離≤k的兩點共有多少對?(n≤1e4)

題解:(1)首先找到樹a的重心,(重心指的是乙個節點,將該節點刪去之後剩下的最大子樹的節點數最小)將其作為樹a的根。在數列的分治之中我們是直接去區間的中間為分界線,但是這裡,如果隨意將某個點刪去,最壞的情況可能是這棵樹退化成一條鏈,遞迴的深度變成了o(n)。而將重心刪去後,剩餘子樹中的最大子樹大小≤n/2(必然),遞迴深度為o(logn)。

(2)找到樹a重心為根節點之後,將其劃分為以兒子為根的眾多子樹,再新增乙個只有乙個節點子樹,該節點直接連線根節點且邊權為0。計算滿足答案且經過根節點的(i,j)的對數(前面新增的單節點子樹就是計算根節點到其餘節點的答案)累加到ans。但是這裡面得到的***含了來自同一棵子樹的(i,j),因為他們經過了根節點的路徑並不是最短路,而在後續的子問題解決中還會計算到,因此在這裡需要減去來自同一棵子樹的(i,j)的答案數。

(3)將根節點刪去之後,分治解決子樹相同問題

原始碼如下

#include #include #include #include #include #define pb push_back

using namespace std;

const int maxn = 10005, inf = 0x3f3f3f3f;

struct edge

edge[maxn<<1];

int head[maxn], cnt;

void addedge(int u, int v, int w)

int n, k, ans;

int num[maxn];//num[i]表示以i為根的樹 的節點數

int dp[maxn];//一維滾動。dp[i]將i刪去後 當前樹的最大聯通塊的大小

int stack[maxn], top1;//棧

bool vis[maxn] ;//true表刪除

int size;//當前樹的節點數

int root;//根節點

void get_root(int u, int fa)//深搜遍歷的同時找尋樹的重心root

} //最大聯通塊還有可能是當前樹刪除子樹u之後的樹大小

dp[u] = max(dp[u] , size - num[u]);

//最後跟目前的重心對比

if(dp[u]k && j>i) j--;

if(i==j) break ;

ans += j-i;

//stack[i+1]~stack[j]都是滿足條件的

} return ans;

}void solve(int u, int fa)

stack[++top1] = root;

dist[root ] = 0;

sort(stack+1, stack+top1+1, cmp);

ans += calc(1, top1);

vis[root] = 1;

for(int i = head[root]; ~i; i = edge[i].next)

if(!vis[edge[i].to])

solve(edge[i].to, root);//分治解決

}void init()

int main()

num[1] = n;

solve(1, 0);

printf("%d\n", ans);

} return 0;

}

分治入門 平面分治

分治思想 劃分子問題,解決子問題,合併子問題 題目 uva 10245 題意 在乙個二維平面內給定n個點,求最近的兩個點的距離。n 10000 題解 直接暴力列舉所有點是肯定行不通的。那麼基於分治的思想 按照橫座標排序後,分成兩個部分,那麼最近距離的點對就是以下的情況 1 兩個點均屬於乙個區域 2 ...

點分治(樹分治)

將原問題分解成若干相同形式,相互獨立的子問題,各個擊破 一般用來解決有關樹上路徑的統計和詢問 p4178 tree 給定一棵 n 個節點的樹,每條邊有邊權,求出樹上兩點距離小於等於 k 的點對數量。暴力做法 o n2 點分治做法 選擇乙個點作為分治中心,令其為rt做dfs。對於一條路徑path u,...

Tree(樹分治 點分治)

原題 poj 1741 題意 有一棵n個節點的樹,每條邊都有乙個權值,問有多少個節點之間的距離小於等於k,解析 典型的樹分治,對於每一棵樹,我們首先找到它的重心 重心 一棵樹中以這個點為root時的最大子樹的節點數最小 int siz n maxn n 這棵子樹大小,最大子樹大小 void getg...