BZOJ 1468:Tree
Description
给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K
Input
N 接下来n-1行边描述管道,按照题目中写的输入 接下来是k
Output
一行,有多少对点之间的距离小于等于k
Sample Input
7
1 6 13
6 3 9
3 5 7
4 1 3
2 4 20
4 7 2
10
Sample Output
5
HINT
n<=40000
Solution
每次把root为根所有子树节点的距离dis算出来
然后答案就是把所有子树节点的距离dis排序然后cal得到ans1
然后每个子树所有点再分别cal一次
以root为根的答案就是ans1-ans2
把所有这些加起来即可
吓死我了开始看了一遍查出好多错...果然模板以后还是自己打比较好
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 400001
using namespace std;
struct edge{
int to,lst,c;
}e[maxn*2];
int last[maxn],tot,son[maxn],n,f[maxn],root,d[maxn],deep[maxn],K,sum;
bool vis[maxn];
void getroot(int poi,int lst)
{
son[poi]=1;f[poi]=0;
for(int i=last[poi];i;i=e[i].lst)
if(e[i].to!=lst && !vis[e[i].to])
{
getroot(e[i].to,poi);
son[poi]+=son[e[i].to];
f[poi]=max(f[poi],son[e[i].to]);
}
f[poi]=max(f[poi],n-son[poi]);
if(f[root]>f[poi])root=poi;
}
void getdeep(int poi,int lst)
{
deep[++deep[0]]=d[poi];
for(int i=last[poi];i;i=e[i].lst)
if(e[i].to!=lst && !vis[e[i].to])
{
d[e[i].to]=d[poi]+e[i].c;
getdeep(e[i].to,poi);
}
}
int getsum(int poi,int now)
{
d[poi]=now;deep[0]=0;
getdeep(poi,0);
sort(1+deep,1+deep+deep[0]);
int t=0,l,r;
for(int l=1,r=deep[0];l<r;)
{
if(deep[l]+deep[r]<=K)t+=r-l,l++;
else r--;
}
return t;
}
void solve(int poi)
{
vis[poi]=1;
sum+=getsum(poi,0);
for(int i=last[poi];i;i=e[i].lst)
if(!vis[e[i].to])
{
sum-=getsum(e[i].to,e[i].c);
n=son[e[i].to];
root=0;
getroot(e[i].to,poi);
solve(root);
}
}
void add(int u,int v,int c)
{
e[++tot]=(edge){v,last[u],c};last[u]=tot;
e[++tot]=(edge){u,last[v],c};last[v]=tot;
}
int main()
{
int k,m;
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int u,v,c;
scanf("%d%d%d",&u,&v,&c);
add(u,v,c);
}
f[root]=100000;
scanf("%d",&K);
getroot(1,0);
solve(root);
printf("%d",sum);
return 0;
}