分治思想:劃分子問題,解決子問題,合併子問題
題目: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...