树分治
树的重心
我们选取一个点,要求将其删去后,结点最多的树的结点个数最小,这个点被
称为“树的重心”。
POJ1644
// Size[u] 代表以节点u为根的子树节点个数
// dp[u] 代表去除u节点后最大子树的节点个数
const int maxn = 2e4+100;
vector<int> G[maxn];
int dp[maxn];
int Size[maxn];
int n;
int ans;
void dfs(int u,int fa){
dp[u] = Size[u] = 0;
for(int i = 0;i < G[u].size(); ++i){
if(fa==G[u][i])continue;
dfs(G[u][i],u);
// sum += tmp;
Size[u] += Size[G[u][i]];
dp[u] = max(dp[u],Size[G[u][i]]);
}
Size[u]++;
dp[u] = max(n-Size[u],dp[u]);
if(dp[u] < dp[ans]) ans = u;
}
int main(void)
{
int T;
cin>>T;
while(T--){
scanf("%d",&n);
for(int i = 1;i <= n; ++i) G[i].clear();
for(int i = 1;i <= n-1; ++i){
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
ans = 0;
dp[0] = INF;
dfs(1,-1);
printf("%d %d\n",ans,dp[ans]);
}
return 0;
}
应用树的重心的特性,即从树的重心开始树的点分治求解一些关于树的分治有关的问题
POJ1741
typedef pair<int,int> P;
const int maxn = 1e4+100;
vector<P> G[maxn];
int n,k;
int size[maxn];
int dp[maxn];
bool vis[maxn];
int depth[maxn];
int deep[maxn];
// 求重心
void getroot(int u,int fa,int allnode,int &root){
size[u] = 1;
dp[u] = 0;
for(int i = 0;i < G[u].size(); ++i){
int v = G[u][i].FI;
if(vis[v] || v == fa) continue;
getroot(v,u,allnode,root);
size[u] += size[v];
dp[u] = max(dp[u],size[v]);
}
dp[u] = max(dp[u],allnode-size[u]);
if(dp[u] < dp[root]) {root = u;}
}
void dfsdepth(int u,int fa){
deep[++deep[0]] = depth[u];
size[u] = 1;
for(int i = 0;i < G[u].size(); ++i){
int v = G[u][i].FI;
if(vis[v]||v == fa) continue;
depth[v] = depth[u] + G[u][i].SE;
dfsdepth(v,u);
size[u] += size[v];
}
}
int cal(int u,int d){
depth[u] = d;
deep[0] = 0;
dfsdepth(u,-1);
sort(deep+1,deep+deep[0]+1);
int ans = 0;
for(int l = 1, r = deep[0];l < r; ){
if(deep[l]+deep[r] <= k){ ans += r-l; l++;}
else r--;
}
return ans;
}
int work(int u){
vis[u] = 1;
int ans = 0;
// dfssize(u,-1);
ans += cal(u,0);
for(int i = 0;i < G[u].size(); ++i){
int v = G[u][i].FI;
if(vis[v]) continue;
ans -= cal(v,G[u][i].SE);
int root = 0;
getroot(v,u,size[v],root);
// cout<<root<<endl;
ans += work(root);
}
return ans;
}
int main(void)
{
while(scanf("%d%d",&n,&k)!=EOF&&n&&k){
memset(vis,0,sizeof(vis));
for(int i = 1;i <= n; ++i) G[i].clear();
for(int i = 1;i < n; ++i){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
G[u].Pb(P(v,w));
G[v].Pb(P(u,w));
}
int root = 0;
dp[root] = INF;
getroot(1,-1,n,root);
int ans = work(root);
printf("%d\n",ans);
}
return 0;
}