樹分治入門 POJ1741,hdu5977

2022-08-15 06:36:17 字數 4053 閱讀 9088

經常發現一些布布扣一類的**直接爬csdn的blog,娘的

對於樹上的路徑問題,一種高效的處理方式就是分治演算法。關於樹分治演算法的研究,詳見2023年ioi國家集訓隊**——《分治演算法在樹的路徑問題中的應用》。

通常對於樹上的分治演算法有兩種,第一種是針對點進行的分治,另一種是針對邊進行的分治,可以證明,大部分情況下點分治演算法的效能更加穩定,而邊分治在某些情況下,演算法效率非常低。所以以下主要討論點分治。

如poj-1741,求解一棵樹中路徑長度不大於k的有多少點對。

對於一棵有根樹,樹中滿足對所對應的一條路徑,必然是以下兩種情況之一:

1.經過根節點

2.路徑在以根節點某個兒子為根的一棵子樹中

對於情況2,可以遞迴求解,下面主要來考慮情況1.

那麼對於這道題的情況1,設dis[i]為節點i到根的距離,我們就是要求解有多少對經過根的路徑,那麼問題等價於能找到多少對不同的點對(i,j),使得dis[i]+dis[j]<=k,而且i和j要屬於以當前根的兩個不同的兒子為根的子樹中。

將問題轉化以下,就可以發現所求結果等價於在一棵有根樹中找到的點對數x-在以當前根的兒子為根的子樹所找到的點對數。

求x、y的過程均可以轉化為以下問題:已知a[1],a[2],…a[m],求滿足i

**裡第一道例題

參考了網上的兩個板

kuangbin的solve寫的很csy,這一部分用了這個

#include 

#include

#include

#include

using

namespace

std;

const

int n = 1e4 + 7;

const

int inf = 0x3f3f3f3f;

int n, k, ans;

struct edge

edge (int f, int t, int _w, int n):from(f),to(t),w(_w),nxt(n){}

} edges[n * 2];

bool vis[n];

int head[n], e, siz[n], dep[n];

void init()

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

int dfssize(int u, int pre)

return siz[u];

}//找重心

void dfsroot(int u, int pre, int totnum, int &minn, int &root)

if(maxx < minn)

}//求每個點離重心的距離

void dfsdep(int u,int pre,int dist, int &num)

}//計算以u為根的子樹中有多少點對的距離小於等於k

int calc(int u, int d)

void solve(int u)

}int main()

ans = 0;

solve(1);

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

}return

0;}

現場的時候k = 7,樹形dp可以水過,重現賽的時候k改為了10

需要樹分治,裸的樹分治(如上),統計距離小於等於k的點對數

本題問路徑經包含所有顏色的點對數

利用位運算狀壓,k種顏色,k<=10,每種顏色佔一位

原題轉化為統計路徑 or運算和 == fullk的點對數

fullk =(1<
poj1741通過乙個sort和兩個指標統計出了dist<=k的點對數

這裡是二進位制位,sort不靈了,需要另一種nlogn的方案

對於每點u的狀態dep[u],列舉其每個子集s0,ans += hash[fullk ^ so]

列舉子集一開始也不會,很玄學,這裡有一些講解,騷到了

位運算狀壓其實很好想,把原來的加號改成or運算就好了

很騷的一段就是calc中的一段,從這裡再次盜個圖來,舉了個列舉子集的例子

我見他**第一行是

/**此**借鑑了某大神的,我看懂了後又分析的*/
似乎他也參考了這個blog

扯了一堆有的沒的

最後貼自己的**吧,用上題的板子改,dfsdep和dfs看著改就好,calc動些腦子

#include 

#include

#include

#include

#include

using

namespace

std;

typedef

long

long ll;

const

int n = 5e4 + 7;

const

int inf = 0x3f3f3f3f;

int n, k;

ll ans;

int col[n], fullk;

ll hashs[1200];

struct edge

edge (int f, int t, int _w, int n):from(f),to(t),w(_w),nxt(n){}

} edges[n * 2];

bool vis[n];

//本題中邊無w,dep為節點到root col or運算和

int head[n], e, siz[n], dep[n];

void init()

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

int dfssize(int u, int pre)

return siz[u];

}//找重心

void dfsroot(int u, int pre, int totnum, int &minn, int &root)

if(maxx < minn)

}//求每個點離重心的距離

void dfsdep(int u, int pre, int dist, int &num)

hashs[dep[i]]++;

}return ans;

}void dfs(int u)

}int main()

for (int i = 1; i < n; i++)

ans = 0;

if (k == 1) ans = (ll)n * (ll)n;

else dfs(1);

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

}return

0;}

poj2531,給個20個點的圖,問最小割

還有點迷糊這個怎麼就能剪枝了

#include 

#include

using

namespace

std;

int main()

這題正解好像是dfs

不過,隨機化亂搞似乎也可以,亂搞好玩啊,here

操作1e5次

對於每一次操作,隨機選擇乙個點,把他放到另乙個集合裡面去,然後for一遍,算出當前的割值sum,再與記錄的最大值ans比較更新

#include 

#include

#include

#include

using

namespace

std;

int n, g[22][22];

bool go[22];

int main()

ans = max(sum, ans);

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

}return

0;}

樹分治入門 POJ1741,hdu5977

經常發現一些布布扣一類的 直接爬csdn的blog,娘的 對於樹上的路徑問題,一種高效的處理方式就是分治演算法。關於樹分治演算法的研究,詳見2009年ioi國家集訓隊 分治演算法在樹的路徑問題中的應用 通常對於樹上的分治演算法有兩種,第一種是針對點進行的分治,另一種是針對邊進行的分治,可以證明,大部...

POJ 1741 樹分治入門

include include include include using namespace std const int inf 0x3f3f3f3f const int max 1e4 5 點分治 cursize 樹當前大小 curroot 當前樹的根 son i 節點i的子節點的個數 d i ...

POJ 1741 樹的分治

題意就是求樹上距離小於等於k的點對有多少個 n2的演算法肯定不行,因為1w個點 這就需要分治。可以看09年漆子超的 本題用到的是關於點的分治。乙個重要的問題是,為了防止退化,所以每次都要找到樹的重心然後分治下去,所謂重心,就是刪掉此結點後,剩下的結點最多的樹結點個數最小。每次分治,我們首先算出重心,...