您的位置:首页 > 其它

POJ 1741 Tree 点分治

2016-08-31 15:04 344 查看
时空隧道

题目大意:

给出一棵树,求出这棵树上满足要求的点对有多少个—-要求是两点之间距离小于等于k

分析:

第一想法是n^2暴力,但是很不幸,n<=10000,n^2炸了….

怎么办呢….

我们如果把这棵树看成一棵有根树,那么这些点对有两种情况—-一种是两个点在两棵子树中,一种是在一颗子树中…

所以我们可以求出dis[i](所有点到重心的距离—为什么是重心呢…防止退化成n^2),然后在dis数组中On的求出满足要求的点对,然后递归处理它的子树,但是这样会有重复的,所以要减去子树中的ans,也就是说只有路径经过重心的点对才会对答案有贡献

代码如下:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=10000+5;
int hd[maxn],to[maxn*2],nxt[maxn*2],w[maxn*2],cnt,n,barycentre,MIN,ans,dis[maxn],size[maxn],MAX[maxn],num,k;
bool vis[maxn];
inline void add(int x,int y,int s){
w[cnt]=s;
to[cnt]=y;
nxt[cnt]=hd[x];
hd[x]=cnt++;
}
inline void SIZE(int root,int fa){
size[root]=1,MAX[root]=0;
for(int i=hd[root];i!=-1;i=nxt[i])
if(to[i]!=fa&&!vis[to[i]])
SIZE(to[i],root),size[root]+=size[to[i]],MAX[root]=max(MAX[root],size[to[i]]);
}
inline void findroot(int u,int root,int fa){
MAX[root]=max(MAX[root],size[u]-size[root]);
if(MIN>MAX[root])
MIN=MAX[root],barycentre=root;
for(int i=hd[root];i!=-1;i=nxt[i])
if(!vis[to[i]]&&to[i]!=fa)
findroot(u,to[i],root);
}
inline void DIS(int root,int fa,int d){
dis[++num]=d;
for(int i=hd[root];i!=-1;i=nxt[i])
if(to[i]!=fa&&!vis[to[i]])
DIS(to[i],root,d+w[i]);
}
inline int calc(int root,int d){
num=0;
DIS(root,-1,d);
sort(dis+1,dis+num+1);
int res=0,x=1,y=num;
while(x<y){
while(x<y&&dis[x]+dis[y]>k)
y--;
res+=y-x;
x++;
}
return res;
}
inline void dfs(int root){
MIN=n;
SIZE(root,-1);
findroot(root,root,-1);
ans+=calc(barycentre,0);
vis[barycentre]=1;
for(int i=hd[barycentre];i!=-1;i=nxt[i])
if(!vis[to[i]])
ans-=calc(to[i],w[i]),dfs(to[i]);
}
signed main(void){
while(scanf("%d%d",&n,&k)&&!(!n&&!k)){
ans=cnt=0,memset(hd,-1,sizeof(hd));
for(int i=1,x,y,s;i<n;i++)
scanf("%d%d%d",&x,&y,&s),add(x,y,s),add(y,x,s);
memset(vis,0,sizeof(vis));
dfs(1);
printf("%d\n",ans);
}
return 0;
}


by >_< neighthorn
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: