题意:
给出一颗树,询问有多少对点对距离<=k
链接:
http://poj.org/problem?id=1741
题解:
点分治的模板题
点分治即采用分治思想分而治之
考虑一颗子树内距离<=k的两种情况
1.这两点连线不过根节点、
那么就是这个问题的一个子问题
2.这两点连线过根节点
那么从根节点开始dfs出deep数组
之后只需将deep数组排序,一个指针从head开始,一个指针从tail开始,只需满足dep[x]+dep[y]<=k即为满足的解
但会发现如果两个节点位于同一颗子树中是不能构成的,所以应dfs减去这些答案
**读入要优化
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<ctime> #include<algorithm> using namespace std; #define maxn 110000 #define INF 98937894 int root,n,m,c,dd,e,l,ans,sum,b[maxn],head[maxn],son[maxn],f[maxn],vis[maxn],deep[maxn],d[maxn]; struct re{int a,b,c;}a[maxn*2]; void arr(int x,int y,int z) { l++; a[l].a=head[x]; a[l].b=y; a[l].c=z; head[x]=l; } void getroot(int x,int fa) { son[x]=1; f[x]=0; int u=head[x]; while (u!=0) { int v=a[u].b; if (!(v==fa||vis[v])) { getroot(v,x); son[x]+=son[v]; f[x]=max(f[x],son[v]); } u=a[u].a; } f[x]=max(f[x],sum-son[x]); if (f[x]<f[root]) root=x; } void getdeep(int x,int fa) { deep[++deep[0]]=d[x]; int u=head[x]; while (u!=0) { int v=a[u].b; if (!(v==fa||vis[v])) { d[v]=d[x]+a[u].c; getdeep(v,x); } u=a[u].a; } } int cal(int x,int v) { d[x]=v; deep[0]=0; getdeep(x,0); sort(deep+1,deep+deep[0]+1); int l=1,r=deep[0],sum=0; while (l<r) { if (deep[l]+deep[r]<=m) sum+=r-l,l++; else r--; } return sum; } void solve(int x) { ans+=cal(x,0); vis[x]=1; int u=head[x]; while (u!=0) { int v=a[u].b; if (vis[v]!=1) { ans-=cal(v,a[u].c); sum=son[v]; root=0; getroot(v,x); solve(root); } u=a[u].a; } } int main() { freopen("noip.in","r",stdin); freopen("noip.out","w",stdout); std::ios::sync_with_stdio(false); cin>>n>>m; while (n!=0) { memset(vis,0,sizeof(vis)); memset(head,0,sizeof(head)); l=ans=root=0; f[0]=INF; for (int i=1;i<=n-1;i++) { cin>>c>>dd>>e,arr(c,dd,e),arr(dd,c,e); } sum=n; getroot(1,0); solve(root); cout<<ans<<endl; cin>>n>>m; } return 0; }