题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3365
题解:点分治的题
(对于点分治初学者,简单概括一下点分治):点分治通过递归的方式不断找到子树的重心,然后以当前的子树的重心为根,统计该子树内所有点到根(重心)的距离并存放到一个数组里,然后在距离数组里做一些事情。点分治的时间复杂度为nlogn*(对距离数组做的事情的时间复杂度)
树的重心:找到一个点,使其作为树根,其最大子树的节点个数最少
说这道题:点分治裸题,把距离数组排序后用双指针l,rO(n)扫描,对于每一个l,有多少个l<i<=r使得len[i]+len[l]<=k,最后输出答案(因为点分治这种做法会有在同一颗子树的两个距离被计算入答案的情况,所以要把其所有子树的答案删除
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf=1e9+7;
const int N=80005;
const int M=160005;
int maxn,siz[N],s[N],dis[N];
int n,m,K,cnt,G,sum,ans,h,lj[N],nxt[M],w[M],to[M];
bool vis[N];
void ins(int f,int t,int p) {cnt++,to[cnt]=t,nxt[cnt]=lj[f],lj[f]=cnt,w[cnt]=p;}
void add(int f,int t,int p) {ins(f,t,p),ins(t,f,p);}
void findG(int x,int fa)
{
siz[x]=1;
int mx=0;
for(int i=lj[x];i;i=nxt[i])
if(to[i]!=fa&&!vis[to[i]])
{
findG(to[i],x);
siz[x]+=siz[to[i]];
mx=max(mx,siz[to[i]]);
}
mx=max(mx,sum-siz[x]);
if(mx<maxn) maxn=mx,G=x;
}
void dfs(int x,int fa)
{
s[++h]=dis[x];
for(int i=lj[x];i;i=nxt[i])
if(to[i]!=fa&&!vis[to[i]])
{
dis[to[i]]=dis[x]+w[i];
dfs(to[i],x);
}
}
int cal(int x,int now)
{
dis[x]=now;
h=0;
dfs(x,0);
sort(s+1,s+h+1);
int ans=0,l=1,r=h;
while(l<r)
{
if(s[l]+s[r]<=K) ans+=r-l,l++;
else r--;
}
return ans;
}
void solve(int x)
{
ans+=cal(x,0);
vis[x]=1;
for(int i=lj[x];i;i=nxt[i])
if(!vis[to[i]])
{
ans-=cal(to[i],w[i]);
G=0;
maxn=inf;
sum=siz[to[i]];
findG(to[i],0);
solve(G);
}
}
int main()
{
scanf("%d%d",&n,&m);
int u,v,w;
char ss[5];
for(int i=1;i<=m;i++)
{
scanf("%d%d%d%s",&u,&v,&w,&ss);
add(u,v,w);
}
scanf("%d",&K);
G=0;
sum=n,maxn=inf;
findG(1,0);
solve(G);
printf("%d\n",ans);
}