题目描述
给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K
输入输出格式
输入格式:
N(n<=40000) 接下来n-1行边描述管道,按照题目中写的输入 接下来是k
输出格式:
一行,有多少对点之间的距离小于等于k
输入输出样例
输出样例#1:
5
题解:点分裸题,考虑分治中的暴力,将所有的重心子树中的点到中心的距离排序,对于一组l-r之间如果d[l]+d[r]<=k,显然d[l]+d[i](i<r)时都满足d[l]+d[i]<=k,可以统计答案,类似尺取的思想。
总复杂度是O(nlognlogn)
代码如下:
#include<map> #include<set> #include<queue> #include<cmath> #include<cstdio> #include<string> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #define mp make_pair #define pii pair<int,int> using namespace std; vector<pii> g[40010]; int n,k,f[40010],vis[40010],size[40010],di[40010],cnt,ans; void get_size(int now,int fa) { size[now]=1; f[now]=fa; for(int i=0;i<g[now].size();i++) { if(vis[g[now][i].first]||g[now][i].first==fa) continue; get_size(g[now][i].first,now); size[now]+=size[g[now][i].first]; } } int get_zx(int now,int fa) { if(size[now]==1) return now; int son,maxson=-1; for(int i=0;i<g[now].size();i++) { if(vis[g[now][i].first]||g[now][i].first==fa) continue; if(maxson<size[g[now][i].first]) { maxson=size[g[now][i].first]; son=g[now][i].first; } } int zx=get_zx(son,now); while(size[zx]<(size[now]-size[zx])*2) zx=f[zx]; return zx; } void get(int now,int fa,int dis) { di[++cnt]=dis; for(int i=0;i<g[now].size();i++) { if(vis[g[now][i].first]||g[now][i].first==fa) continue; get(g[now][i].first,now,dis+g[now][i].second); } } int calc(int now,int dis) { cnt=0; int tmp=0; get(now,0,dis); sort(di+1,di+cnt+1); int l=1,r=cnt; while(l<=r) { if(di[l]+di[r]<=k) { tmp+=r-l; l++; } else r--; } return tmp; } void solve(int now) { ans+=calc(now,0); vis[now]=1; for(int i=0;i<g[now].size();i++) { if(vis[g[now][i].first]) continue; ans-=calc(g[now][i].first,g[now][i].second); get_size(g[now][i].first,0); int zx=get_zx(g[now][i].first,0); solve(zx); } } int main() { scanf("%d",&n); for(int i=1;i<n;i++) { int from,to,cost; scanf("%d%d%d",&from,&to,&cost); g[from].push_back(mp(to,cost)); g[to].push_back(mp(from,cost)); } scanf("%d",&k); solve(1); printf("%d ",ans); }