Tree
Time Limit: 1000MS Memory Limit: 30000K
Total Submissions: 24713 Accepted: 8256
Description
Give a tree with n vertices,each edge has a length(positive integer less than 1001).
Define dist(u,v)=The min distance between node u and v.
Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not exceed k.
Write a program that will count how many pairs which are valid for a given tree.
Input
The input contains several test cases. The first line of each test case contains two integers n, k. (n<=10000) The following n-1 lines each contains three integers u,v,l, which means there is an edge between node u and v of length l.
The last test case is followed by two zeros.
Output
For each test case output the answer on a single line.
Sample Input
5 4
1 2 3
1 3 1
1 4 2
3 5 1
0 0
Sample Output
8
Source
http://poj.org/problem?id=1741
#include <bits/stdc++.h>
using namespace std;
#define mo 100005
#define mk make_pair
vector< pair<int ,int > > d[mo];//箭头加空格
int sz[mo];//树的重心..
int sd[mo];//树的重心..
int dist[mo];//距离重心的距离
int jl[mo];//删重心的标记
void dfsz(int node,int dad,int &maxs,int &v,int n) //寻找重心,n是计算树的大小得出
{
sz[node]=1;
for(int i=0;i<d[node].size();i++)
{
int son=d[node][i].first;
if(son!=dad&&!jl[son])
{
dfsz(son,node,maxs,v,n);
sz[node]+=sz[son];
if(sz[son]<maxs)
{
maxs=sz[son];
v=son;
}
}
}
if(n-sz[node]<maxs)
{
maxs=n-sz[node];
v=node;
}
}
void dis(int node,int dad,int s,int &l)//求其它点到重心的距离
{
dist[l++]=s;
for(int i=0;i<d[node].size();i++)
{
int son=d[node][i].first;
int ls=d[node][i].second;
if(son!=dad&&!jl[son])
dis(son,node,s+ls,l);
}
}
int shu(int k,int v,int d)//数数有多少歌k对
{
int l=0;
dis(v,0,d,l);
sort(dist,dist+l);
int i=0,j=l-1;
int ans=0;
while(i<j)
{
while(dist[i]+dist[j]>k) j--;
ans+=j-i;
i++;
}
return ans;
}
void dx(int node,int dad,int &l)//计算树的大小,为了算树的重心
{
l++;
for(int i=0;i<d[node].size();i++)
{
int son=d[node][i].first;
if(son!=dad&&!jl[son])
dx(son,node,l);
}
}
void dfs(int x,int &ans,int k)//递归分治
{
int n=0;
dx(x,0,n);
int mins=10000005,v=x;
dfsz(x,0,mins,v,n);
ans+=shu(k,v,0);
jl[v]=1;
for(int i=0;i<d[v].size();i++)
{
int son=d[v][i].first;
if(!jl[son])
{
int le=d[v][i].second;
ans-=shu(k,son,le);
dfs(son,ans,k);
}
}
}
int main()
{
int n,k;
while(cin>>n>>k&&n+k)
{
int x,y,z;
for(int i=1;i<n;i++)
{
cin>>x>>y>>z;
d[x].push_back(mk(y,z));
d[y].push_back(mk(x,z));
}
int ans=0;
dfs(1,ans,k);
cout<<ans<<endl;
}
}
P3806 【模板】点分治1
题目背景
感谢hzwer的点分治互测。
题目描述
给定一棵有n个点的树
询问树上距离为k的点对是否存在。
输入输出格式
输入格式:
n,m 接下来n-1条边a,b,c描述a到b有一条长度为c的路径
接下来m行每行询问一个K
输出格式:
对于每个K每行输出一个答案,存在输出“AYE”,否则输出”NAY”(不包含引号)
输入输出样例
输入样例#1:
2 1
1 2 2
2
输出样例#1:
AYE
说明
对于30%的数据n<=100
对于60%的数据n<=1000,m<=50
对于100%的数据n<=10000,m<=100,c<=1000,K<=10000000
https://www.luogu.org/problem/show?pid=3806
#include <bits/stdc++.h>
using namespace std;
#define mo 100005
#define mk make_pair
vector< pair<int ,int > > d[mo];//箭头加空格
int sz[mo];//树的重心..
int sd[mo];//树的重心..
int dist[mo];//计算到重心的距离
int shux[mo];//标记点是从哪个子树下的
bool de[10000005];//标记k是否出现过
int jl[mo];//删除树的重心标记
int zz;
void dfsz(int node,int dad,int &maxs,int &v,int n) //计算树的重心
{
sz[node]=1;
for(int i=0;i<d[node].size();i++)
{
int son=d[node][i].first;
if(son!=dad&&!jl[son])
{
dfsz(son,node,maxs,v,n);
sz[node]+=sz[son];
if(sz[son]<maxs)
{
maxs=sz[son];
v=son;
}
}
}
if(n-sz[node]<maxs)
{
maxs=n-sz[node];
v=node;
}
}
void dis(int node,int dad,int s,int &l,int j) //计算点到重心的距离
{
dist[l]=s;
shux[l]=j;//标记属于哪个子树
l++;
if(dad==0) j=2;
for(int i=0;i<d[node].size();i++)
{
int son=d[node][i].first;
int ls=d[node][i].second;
if(dad==0) j++;//只有dad为初始值的时候分子树编号
if(son!=dad&&!jl[son])
{
dis(son,node,s+ls,l,j);
}
}
}
int shu(int k,int v,int d)//标记k是否出现过
{
int l=0;
memset(shux,0,sizeof(shux));
dis(v,0,d,l,0);
for(int i=0;i<l;i++)
{
for(int j=i+1;j<l;j++)
{
if(shux[i]!=shux[j])//只有不同的子树可以相加
{
//cout<<shux[i]<<' '<<shux[j]<<' '<<dist[i]<<' '<<dist[j]<<' '<<dist[i]+dist[j]<<' '<<i<<' '<<j<<endl;
de[dist[i]+dist[j]]=1;
}
}
}
return 0;
}
void dx(int node,int dad,int &l)//计算树的大小
{
l++;
for(int i=0;i<d[node].size();i++)
{
int son=d[node][i].first;
if(son!=dad&&!jl[son])
dx(son,node,l);
}
}
void dfs(int x,int &ans,int k)//树的分治递归
{
int n=0;
dx(x,0,n);//首先判断大小
int mins=10000005,v=x;
dfsz(x,0,mins,v,n);//之后机选树的重心
shu(k,v,0); //再去寻找距离重心的距离和距离为k 的数对
jl[v]=1;
for(int i=0;i<d[v].size();i++)
{
int son=d[v][i].first;
if(!jl[son])
{
int le=d[v][i].second;
//ans-=shu(k,son,le);
dfs(son,ans,k);
}
}
//jl[v]=0;
}
int main()
{
int n,m,k;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(de,false,sizeof(de));
int x,y,z;
for(int i=1;i<n;i++)
{
//cin>>x>>y>>z;
scanf("%d%d%d",&x,&y,&z);
d[x].push_back(mk(y,z));
d[y].push_back(mk(x,z));
}
int ans=0;
dfs(1,ans,k);
for(int i=0;i<m;i++)
{
//cin>>k;
scanf("%d",&k);
if(de[k]) //cout<<"AYE"<<endl;
printf("AYE\n");
else //cout<<"NAY"<<endl;
printf("NAY\n");
}
}
}