点分治是针对树上路径所提出的算法.
首先先找重心,重心指的是把该点删去使得各连通块大小最大值最小的点.
每一条路径都有且只有两种情况:
1. 经过重心,这里我们直接每次更新deep值,直接算.如果存在某个i,j其到重心的路径有重合,即这是一条非简单路径,那么我们递归子问题然后减去就行,也算是一种容斥吧
2. 不经过重心,直接递归求解了.
BZOJ1468
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define maxn 40010
#define inf 0x3f3f3f3f
using namespace std;
struct node{
int to,wi;
node*next;
}*con[maxn];
int n,sz[maxn],f[maxn],deep[maxn],de[maxn],sum,tail,root,k,ans;
bool vis[maxn];
void addedge(int x,int y,int w)
{
node*p=new node;
p->to=y;
p->wi=w;
p->next=con[x];
con[x]=p;
}
void get_root(int v,int fa)
{
sz[v]=1;f[v]=0;
for(node*p=con[v];p;p=p->next)
if(p->to!=fa&&!vis[p->to]){
get_root(p->to,v);
sz[v]+=sz[p->to];
f[v]=max(f[v],sz[p->to]);
}
f[v]=max(f[v],sum-f[v]);
if(f[v]<f[root]) root=v;
}
void get_dep(int v,int fa)
{
deep[++tail]=de[v];
for(node*p=con[v];p;p=p->next)
if(!vis[p->to]&&p->to!=fa)
de[p->to]=de[v]+p->wi,get_dep(p->to,v);
}
int cal(int v,int w)
{
tail=0;de[v]=w;get_dep(v,0);
int tmp=0;
sort(deep+1,deep+tail+1);
for(int l=1,r=tail;l<r;)
if(deep[l]+deep[r]<=k) tmp+=r-l,l++;
else r--;
return tmp;
}
void work(int v)
{
ans+=cal(v,0);
vis[v]=1;
for(node*p=con[v];p;p=p->next)
if(!vis[p->to]){
ans-=cal(p->to,p->wi);
sum=sz[p->to];
root=0;
get_root(p->to,root);
work(root);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;++i)
{
int x,y,w;
scanf("%d%d%d",&x,&y,&w);
addedge(x,y,w);addedge(y,x,w);
}
scanf("%d",&k);
root=0;sum=n;f[0]=inf;
get_root(1,0);
work(root);
printf("%d\n",ans);
return 0;
}