难度 尚未评定
Description
给定一颗有个节点的树,每条边有一个权值。树上两个节点和之间的路径长度就是路径上各条边的权值的合。
求树上长度不超过的路径有多少条。
CCYOS
这是一道点分治例题。
- 若指定节点为根,则对于节点,树上路径有两类:
a. 经过根结点。
b. 包含于的某一子树,不经过。- 对于第二类路径,可以视作每颗子树下的子问题递归处理。
对于第一类路径,至多由两段组成:和。求解第一类路径时,要记录数组和数组,分别表示根结点到该点的距离和该点属于哪一棵子树。根据题意,- 构建函数,统计在以p为根的树中满足上述要求的点对个数。
- 实现方法:
一 · 树上直接统计
对于根结点p下的子树为。
对于中的每个节点,将在子树中满足的节点数量加入答案中即可。
可建立树状数组求解。
本做法应用:YALI 0J 2902B 文学
二·指针扫描数组
把树上每个点放入数组,并将数组按照值排序。
使用两个指针从前后扫描数组。
不难发现在向右扫描的过程中,的范围是从右往左单调递减的。(不能加大数了)
另外处理一个数组,维护在之间满足的位置的个数。
则易得当时,答案即为。
本做法应用:POJ 1741 Tree- 总结点分治算法过程:
a.找到树的中心p作为根结点。
b.从p进行dfs,求出rt和d数组。
c.clac(p)统计答案。
d.删除根结点p,对p的每颗子树进行a - d。
code
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
struct EDGE{ int to,next; long long w; }edg[80005];
int n,tot,root,limit,siz,vis[40005],head[40005],size[40005];
long long k,ans,l,r;
long que[40005],dis[40004];
inline void adde(int x,int y,int w){ edg[++tot].to = y; edg[tot].w = w; edg[tot].next = head[x]; head[x] = tot; }
inline void getroot(int p,int fa){
size[p] = 1; int num = 0;//从p点出发的子树大小
for(int i = head[p];i;i = edg[i].next){
int to = edg[i].to;
if(fa == to||vis[to])continue;
getroot(to,p); size[p] += size[to];
num = max(num,size[to]);
}
num = max(num,siz - size[p]);
if(num < limit){//limit:当前最小
limit = num; root = p;
}
}
inline void getdis(int p,int fa){
que[++r] = dis[p];
for(int i = head[p];i;i = edg[i].next){
int to = edg[i].to;
if(to == fa||vis[to])continue;
dis[to] = dis[p] + edg[i].w; getdis(to,p);
}
}
inline long long clac(int p,int v){
r = 0; dis[p] = v;
long long sum = 0;
getdis(p,0);
l = 1;
sort(que + 1,que + r + 1);
while(l < r){
if(que[l] + que[r] <= k)sum += r - l,++l;
else r--;
}
return sum;
}
inline void dfs(int p){
ans += clac(p,0); vis[p] = 1;//将p点作为根结点是否处理完毕
for(int i = head[p];i;i = edg[i].next){
int to = edg[i].to;
if(vis[to])continue;
ans -= clac(to,edg[i].w);
siz = size[to];
limit = 2147483647;
getroot(to,0);
dfs(root);
}
}
inline void init(){
tot = 0;ans = 0;
for(int i = 1;i <= n;++i)head[i] = 0,vis[i] = 0;
}
int main(){
scanf("%d%lld",&n,&k);
int x,y; long long w;
while(n&&k){ for(int i = 1;i < n;++i){
scanf("%d%d%lld",&x,&y,&w);
adde(x,y,w); adde(y,x,w);
}
limit = 2147483647;//
siz = n;//对于第一棵子树的大小限制
getroot(1,0);//找到树的重心,更新root
dfs(root);//从root开始递归统计更新答案
printf("%lld\n",ans); init();
scanf("%d%lld",&n,&k);
}
return 0;
}
网友评论