Poj1741 Tree(树分治)
题目描述
给出一棵树,求出其中有多少点对满足点之间的路径小于某个值K
解题思路
路径可以分成树间路径和树内路径。对于树间路径只需要满足两点到树的根节点的距离之和小于等于k则满足条件。只要对树间路径进行容斥即是答案。为此需要根据根节点进行递归容斥,通过树分治的思想,每次找树的中心进行计算可以将递归次数降到到log(n),算上每次枚举需要对子树的所有边进行排序,因此总的复杂度就是 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
AC代码
#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>
#include<cstdio>
using namespace std;
const int size=1e4+5;
typedef pair<int,int> pii;
vector<pii> G[size];
int d[size],sz[size];
bool vis[size];
int st[size];
vector<int> v;
int ans=0;
int root,k;
void find_root(int u,int f)
{
sz[u]=1;st[u]=0;
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i].first;
int w=G[u][i].second;
if(vis[v]||v==f) continue;
find_root(v,u);
sz[u]=sz[u]+sz[v];
st[u]=max(st[u],sz[v]);
}
st[u]=max(st[u],st[0]-sz[u]);
if(st[u]<st[root]) root=u;
}
void getdis(int u,int f)
{
sz[u]=1;
v.push_back(d[u]);
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i].first,
w=G[u][i].second;
if(v==f||vis[v]) continue;
d[v]=d[u]+w;
getdis(v,u);
sz[u]=sz[u]+sz[v];
}
}
int getans(int u,int blen)
{
v.clear();
d[u]=blen;
getdis(u,0);
sort(v.begin(),v.end());
int tans=0;
for(int l=0,r=v.size()-1;l<r;)
{
if(v[l]+v[r]<=k) tans+=r-l++;
else r--;
}
return tans;
}
void solve(int u)
{
ans+=getans(u,0);
vis[u]=true;
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i].first;
int w=G[u][i].second;
if(vis[v]) continue;
ans-=getans(v,w);
st[0]=sz[v];
find_root(v,root=0);
solve(root);
}
}
int main()
{
int n;
int u,v,w;
while(~scanf("%d%d",&n,&k))
{
if(n==0&&k==0) break;
ans=0;
memset(vis,0,sizeof(vis));
for(int i=0;i<=n;i++) G[i].clear();
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&u,&v,&w);
G[u].push_back(pii(v,w));
G[v].push_back(pii(u,w));
}
st[0]=n;
find_root(1,root=0);
solve(root);
printf("%d\n",ans);
}
}
/*
5 4
1 2 1
2 3 2
3 4 3
4 5 2
5
*/