分析
分治是个好东西,树分治也很妙,其擅长解决树上路径一类的问题
在这里主要是用的点分治(据说比边分治简单)
其实其思想不过也就是将一个大问题,不停的分割分割成子问题,然后需要注意(思考)的就是两个子问题之间产生答案
挪到树上
我们就分为两种情况
- 处理过重心的路径
- 处理子树中的路径
事实证明,TLE从来都不是卡常的锅,你见过哪道题是需要卡常才能A的???
一般TLE都是写挂了……比如在下
代码
此题不开long long也是可以的
#include<cstdio>
#include<cmath>
#include<algorithm>
#define N 10009
#define in read()
#define ll long long
using namespace std;
inline int read(){
char ch;int f=1,res=0;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
while(ch>='0'&&ch<='9'){
res=(res<<3)+(res<<1)+ch-'0';
ch=getchar();
}
return f==1?res:-res;
}
int n,lim;
int nxt[N*2],to[N*2],head[N],w[N*2],ecnt=0;
void add(int x,int y,int z){nxt[++ecnt]=head[x];head[x]=ecnt;to[ecnt]=y;w[ecnt]=z;}
int sze[N],fa[N],d[N],dis[N],son[N],G,minn,num=0;
bool vis[N];
ll ans=0;
void dfssize(int u,int fu){//得到子树大小,及子树中sze最大的那一个
sze[u]=1;son[u]=0;
for(int e=head[u];e;e=nxt[e]){
int v=to[e];
if(vis[v]||(v==fu)) continue;
dfssize(v,u);
sze[u]+=sze[v];
if(sze[v]>son[u]) son[u]=sze[v];
}
}
void dfsG(int rt,int u,int fu){//寻找当前图的重心
if(sze[rt]-sze[u]>son[u]) son[u]=sze[rt]-sze[u];
if(son[u]<minn) minn=son[u],G=u;
for(int e=head[u];e;e=nxt[e]){
int v=to[e];
if(vis[v]||(v==fu)) continue;
dfsG(rt,v,u);
}
}
void dfsdis(int u,int fu){//寻找每个节点到G的距离
d[num++]=dis[u];
for(int e=head[u];e;e=nxt[e]){
int v=to[e];
if(vis[v]||(v==fu)) continue;
dis[v]=dis[u]+w[e];
dfsdis(v,u);
}
}
ll calc(int rt,int L){
ll res=0;num=0;
dis[rt]=L;
dfsdis(rt,0);
sort(d,d+num);
int l=0,r=num-1;
while(l<r){
if(d[l]+d[r]<=lim) {
res+=r-l;//不能算自己
l++;
}
else r--;
}
return res;
}
void solve(int u){
minn=n;
dfssize(u,0);
dfsG(u,u,0);//以 u 为根的子树,寻找当前的重心
vis[G]=1;
ans+=calc(G,0);
for(int i=head[G];i;i=nxt[i]){
if(!vis[to[i]]) {
ans-=calc(to[i],w[i]);
solve(to[i]);
}
}
}
int main(){
while(1){
n=in;lim=in;
if(!n&&!lim) break;
int i,j,k;
num=ecnt=0;
for(i=1;i<=n;++i) vis[i]=0,head[i]=0;
for(i=1;i<n;++i){
int u=in,v=in,z=in;
add(u,v,z);add(v,u,z);
}
ans=0;
solve(1);
printf("%lld\n",ans);
}
return 0;
}