參考**:
這題的兩個關鍵部分: 1. 求出樹的重心(這在之前的部落格裡面提到,不在敘述)
2. 如何進行分治。
首先,如果我們選定乙個點作為根節點,那麼一條路徑要麼經過這個根節點,要麼不經過這個根節點。
題目要求出所有路徑小於等於k的路徑,我們可以這樣考慮(先不考慮重複的),依次把每個點都當做根節點,那麼我們只要dfs一遍就可以求出所有點到根節點的距離,然後考慮兩兩組合,只要符合,我們就統計。
上面的思路必然會有重複的,第一,在同一子樹上的兩個點,都連向根的話,必然重複了一段路徑,所以這部分應該刪除。
所以我們的思路就是先直接求出以乙個點為根節點的所有滿足個數,然後遞迴到子樹,減去在子樹中滿足的(在子樹中滿足,對應於前面的,就是在同一顆子樹重複的)。
對於以下這棵樹:接著就是**了(應該看**更直觀一點)顯然a點是它的重心。
我們假設現在分治到了a點(當前點為a)
我們一開始求解貢獻時,會有以下路徑被處理出來:
a—>a
a—>b
a—>b—>c
a—>b—>d
a—>e
a—>e—>f (按照先序遍歷順序羅列)
那麼我們在合併答案是會將上述6條路徑兩兩進行合併。
這是注意到:
合併a—>b—>c 和 a—>b—>d 肯定是不合法的!!
因為這並不是一條樹上(簡單)路徑,出現了重邊,我們要想辦法把這種情況處理掉。
處理方法很簡單,減去每個子樹的單獨貢獻。
例如對於以b為根的子樹,就會減去:
b—>b
b—>c
b—>d
這三條路徑組合的貢獻
#include#include#include#include#include#include#include#include#include#include#includeusing namespace std;
const int maxn=1e4+7;
const int inf=0x3f3f3f3f;
typedef long long ll;
const int mod=1e9+7;
int n,k,allnode;
int head[maxn*2];
int num;
int dp[maxn];
int size[maxn];
int focus,m;
ll dist[maxn];
int deep[maxn];
bool vis[maxn];
ll ans;
struct edge
edge[maxn<<2];
void addedge(int u,int v,int w)
void init()
void getfocus(int u,int pre)
dp[u]=max(dp[u],allnode-size[u]);
if(m>dp[u])
}void dfs(int u,int pre)
}int cal(int x,int now)
else r--;
}return ans;
}void solve(int x)
}int main()
focus=ans=0;
allnode=n,m=1e9;
getfocus(1,0);
solve(focus);
printf("%lld\n",ans);
}return 0;
}
Tree POJ 1741 點分治 尺取
想到點分治倒是不難,但是怎麼總是tle呢?這個就很煩了,然後就想到了一種把solve 函式裡的那個 尺取的主要方式 while l r else r include include include include include include include include include inc...
POJ 1741 樹的分治
題意就是求樹上距離小於等於k的點對有多少個 n2的演算法肯定不行,因為1w個點 這就需要分治。可以看09年漆子超的 本題用到的是關於點的分治。乙個重要的問題是,為了防止退化,所以每次都要找到樹的重心然後分治下去,所謂重心,就是刪掉此結點後,剩下的結點最多的樹結點個數最小。每次分治,我們首先算出重心,...
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 ...