分治入門——樹分治
分治思想:劃分子問題,解決子問題,合併子問題
題目: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 <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #include <vector> #define pb push_back using namespace std; const int maxn = 10005, inf = 0x3f3f3f3f; struct Edge { int to, next, dist; }edge[maxn<<1]; int head[maxn], cnt; void addedge(int u, int v, int w) { edge[cnt].to = v; edge[cnt].dist = w; edge[cnt].next = head[u]; head[u] = cnt++; } 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 { dp[u] = 0; num[u] = 1;//初始化 for(int i = head[u]; ~i; i = edge[i].next) { int v = edge[i].to; if(v!=fa && !vis[v] ) {//保證不回溯 get_root(v, u); num[u] += num[v];//加上兒子的節點數就是根的節點數 dp[u] = max(num[v], dp[u]);//最大聯通塊大小可能是子樹大小 } } //最大聯通塊還有可能是當前樹刪除子樹u之後的樹大小 dp[u] = max(dp[u] , size - num[u]); //最後跟目前的重心對比 if(dp[u]<dp[root]) root = u; } int dist[maxn];//到根節點的距離 inline bool cmp(int i, int j) { return dist[i] < dist[j]; } void Find_dist(int u, int fa , int d) {//計算當前樹的所有節點到根的距離,並將點入棧 Stack[++top1] = u; dist[u] = d; for(int i = head[u]; ~i ; i = edge[i].next) { int v = edge[i].to; if(vis[v] || v==fa) continue ; Find_dist(v, u, d+edge[i].dist); } } int calc(int l, int r)//計算滿足距離≤k的(i,j)個數 { int j = r, Ans = 0;//此時stack中l到r是按照dist升序排好的,ij一前一後往中間掃 for(int i = l; i <= r; i ++) { while(dist[Stack[i]]+dist[Stack[j]]>k && j>i) j--; if(i==j) break ; Ans += j-i; //Stack[i+1]~Stack[j]都是滿足條件的 } return Ans; } void solve(int u, int fa) { dp[0] = maxn; size = num[u]; root = 0; get_root(u, fa);//找到重心並將之轉換為當前樹的根 top1 = 0; int top2 = 0; for(int i = head[root]; ~i; i = edge[i].next) { int v = edge[i].to; if(vis[v]) continue; top2 = top1; Find_dist(v, root, edge[i].dist); //top2+1 到top1+1為子樹v的所有節點 sort(Stack+top2+1, Stack+top1+1, cmp); ans -= calc(top2+1, top1);//減去在同一棵子樹且經過根節點(即非最短距離)的答案數 } 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() { memset(head, -1, sizeof head); memset(vis, 0, sizeof vis); ans = 0; cnt = 0; } int main() { while(scanf("%d %d", &n, &k), n&&k) { init(); for(int i = 1; i < n; i ++) { int u, v, d; scanf("%d %d %d", &u, &v, &d); addedge(u, v, d); addedge(v, u, d); } num[1] = n; solve(1, 0); printf("%d\n", ans); } return 0; }
相關推薦
分治入門——樹分治
分治思想:劃分子問題,解決子問題,合併子問題 題目:POJ1741 題意:給定一棵含有n個節點的無向帶權樹,滿足距離≤k的兩點共有多少對?(n≤1e4) 題解: (1)首先找到樹A的重心,(重心指的是一個節點,將該節點刪去之後剩下的最大子樹的節點數最小)將其作為
基於點分治的樹分治
algorithm 計算 開始 tails 論文 continue dfsr size 多少 本文代碼來源:https://blog.csdn.net/yang_7_46/article/details/9966455 本文參考論文來源:https://wenku.baid
分治入門——平面分治
分治思想:劃分子問題,解決子問題,合併子問題 題目:UVA 10245 題意:在一個二維平面內給定n個點,求最近的兩個點的距離。(n≤10000) 題解:直接暴力列舉所有點是肯定行不通的。那麼基於分治的思想:按照橫座標排序後,分成兩個部分,那麼最近距離的點
樹分治入門:例題poj1741
ios targe www for define 算法分析 urn article details 題目鏈接:Tree 樹分治參考資料:09年漆子超 ZigZagK的博客 【點分治】的學習筆記和眾多例題 以上的資料是我看過好的,具體算法分析實現都講明白,貼一下我的代碼
poj1741+poj1987+poj2114——點分治入門題集合
剛才 是把 註意 幫我 legend 機房 ini ext one 最近看了看點分治,從poj上找到幾道題,都比較裸。而且感覺這三道題都長得差不多呀qwq ———————————————————————————————————————————————— 【poj 1741】
POJ 1741 男人八題——樹分治
tor pst def roo 容易 air sizeof ram 出發 Tree Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 23829 Accepted
【bzoj1095】[ZJOI2007]Hide 捉迷藏 動態樹分治+堆
max 插入 fine 時間 結構 答案 += oot tdi 題目描述 捉迷藏 Jiajia和Wind是一對恩愛的夫妻,並且他們有很多孩子。某天,Jiajia、Wind和孩子們決定在家裏玩捉迷藏遊戲。他們的家很大且構造很奇特,由N個屋子和N-1條雙向走廊組成,這N-1條
HDU 6183 Color it cdq分治 + 線段樹 + 狀態壓縮
sample div cnblogs 分用 return cmp pac math for each Color it Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 132768/132768 K (J
【bzoj4012】[HNOI2015]開店 動態樹分治+二分查找
oot pri push_back amp data 很多 nbsp targe pac 題目描述 風見幽香有一個好朋友叫八雲紫,她們經常一起看星星看月亮從詩詞歌賦談到人生哲學。最近她們靈機一動,打算在幻想鄉開一家小店來做生意賺點錢。這樣的想法當然非常好啦,但是她們也發現
[BZOJ3295][Cqoi2011]動態逆序對 CDQ分治&樹套樹
的確 分治 mat fda 應該 through tex 瓶頸 這樣的 3295: [Cqoi2011]動態逆序對 Time Limit: 10 Sec Memory Limit: 128 MB Description 對於序列A,它的逆序對數定義為滿足i&
【BZOJ4675】點對遊戲 樹分治+期望
三人 hellip 小數 tro 統計 int 多少 microsoft 題解 【BZOJ4675】點對遊戲 Description 桑尼、露娜和斯塔在玩點對遊戲,這個遊戲在一棵節點數為n的樹上進行。 桑尼、露娜和斯塔三人輪流從樹上所有未被占有的節點中選取一點,歸
【BZOJ3648】寢室管理 樹分治
數組 沒有 != 決定 算法 microsoft put ron 1+n 【BZOJ3648】寢室管理 Description T64有一個好朋友,叫T128。T128是寄宿生,並且最近被老師叫過去當宿管了。宿管可不是一件很好做的工作,碰巧T128有一個工作上的問題
cdq分治入門--BZOJ1176: [Balkan2007]Mokia
。。 入門 矩形 ++ bzoj == img != void 對w*w,w<=2000000的矩形,一開始全是0(或一開始全是s),n<=170000個操作,每次操作:矩陣內某點加上一個數,查某一個子矩陣的和,保證修改數<=160000,詢問數<=
【BZOJ4372】爍爍的遊戲 動態樹分治+線段樹
printf esp ans 當前 sam 註意 與他 dfs 接下來 【BZOJ4372】爍爍的遊戲 Description 背景:爍爍很喜歡爬樹,這嚇壞了樹上的皮皮鼠。題意:給定一顆n個節點的樹,邊權均為1,初始樹上沒有皮皮鼠。爍爍他每次會跳到一個節點u,把周圍
[poj] 1741 Tree || 樹分治
clas com 情況 oid logs algo return sca har 原題 求樹上距離不超過k的點對數。 樹分治的板子題。 每次把一棵樹由重心分為多顆樹,分別遞歸處理。 我們要求的就是不在同一個聯通塊中的符合答案的對數(在同一個的會通過遞歸轉化為不在同一個的)
POJ 1741 Tree | 樹分治
樹分治 等於 namespace poj edge i++ str static tree 求樹上距離小於等於K的點對對數 #include<cstdio> #include<algorithm> #include<cstring>
BZOJ 2152 聰聰可可 | 樹分治
static turn names += efi i++ class -- alc #include<cstdio> #include<algorithm> #include<cstring> #define N 20005 typed
bzoj 1758 [Wc2010]重建計劃 分數規劃+樹分治單調隊列check
href 整數 order font 城市 input ans ref pro [Wc2010]重建計劃 Time Limit: 40 Sec Memory Limit: 162 MBSubmit: 4345 Solved: 1054[Submit][Status][
【CF840D】Destiny 分治(線段樹)
def bool open != ostream for get cto 都在 【CF840D】Destiny 題意:給你一個長度為n的序列,q次詢問,每次指定l r k,求[l,r]中出現次數$>\frac {r-l+1} k$的所有數中最小的那個數。 $n,q
【學術篇】bzoj3262 陌上花開. cdq分治入門
spa pri 一點 markdown 思考 也會 關心 做了 升級 花兒們已經很累了—— 無論是花形、顏色、還是氣味, 都不是為了給人們擺出來欣賞的, 更不是為了當做出題的素材的, 她們並不想自己這些屬性被沒有生命的數字量化, 並不想和其它的花攀比, 並無意分出個三六九等