您的位置:首页 > 其它

POJ1741:Tree——题解+树分治简要讲解

2017-12-14 07:59 387 查看

http://poj.org/problem?id=1741

题目大意:给一棵树,求点对间距离<=k的个数。

————————————————————

以这道题为例记录一下对于树分治的理解。

树分治分为两类,一类是基于点的分治,一类是基于边的分治。

后者与树链剖分很相似,但是一般用不上,这里讲的是前者。

我们一般进行树分治找的点都是这棵树的重心(即子树最大者最小的点),我们每次操作都做与这个点相关的路径,然后删除这个点再重新寻找。

分重心的好处在于我们近似的将树分成了两份,类似于二分,其深度不超过O(logn)(其实有严格证明的,但是我太弱了,不会写)

分完重心的操作大致三种

1.找u,v,其中u,v在重心s的同一棵子树上(这种情况直接忽略,因为看下面的操作我们就能明白我们可以递归的完成这个操作)

2.找u,v,其中u,v在重心s的两棵子树上。

3.找u,查找u到重心s的路径。

我们发现3操作和2操作很相似,我们直接讨论2操作。

显然我们在2操作的路径当中不可避免的要经过s,所以我们从s开始bfs,求出每个点i到s的距离dis[i],我们的路径长度即为dis[u]+dis[v]。

3操作同理只是变成了dis[u]+dis[s],其中dis[s]=0.

这里提供一种简要算法:我们在求完dis之后对我们求的dis排序,这样我们就可以快速的求出点对距离<=k的个数。

但是这样就不可避免的要判重,为什么呢?

废话你这样排不就有可能把1操作的一部分点对先算了一遍,这样明显会导致答案变大。

那怎么办呢?我们对于每一棵子树,再删掉我们通过2操作得到的点对即可。

(现将s删掉,s的儿子dis[u]不变的情况下以u为起点bfs求点对,则这些点对就是在同一棵子树当中被计算的重复的点对,减去即可。)

#include<cmath>
#include<cstdio>
#include<queue>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=10001;
inline int read(){
int X=0,w=0; char ch=0;
while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
struct node{
int w;
int to;
int nxt;
}edge[N*2];
int cnt,n,k,head
,q
,dis
,size
,son
,d
,fa
;
ll ans;
bool vis
;
void add(int u,int v,int w){
cnt++;
edge[cnt].to=v;
edge[cnt].w=w;
edge[cnt].nxt=head[u];
head[u]=cnt;
return;
}
int calcg(int st){
int r=0,g,maxn=n;
q[++r]=st;
fa[st]=0;
for(int l=1;l<=r;l++){
int u=q[l];
size[u]=1;
son[u]=0;
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].to;
if(vis[v]||v==fa[u])continue;
fa[v]=u;
q[++r]=v;
}
}
for(int l=r;l>=1;l--){
int u=q[l],v=fa[u];
if(r-size[u]>son[u])son[u]=r-size[u];
if(son[u]<maxn)g=u,maxn=son[u];
if(!v)break;
size[v]+=size[u];
if(size[u]>son[v])son[v]=size[u];
}
return g;
}
inline ll calc(int st,int L){
int r=0,num=0;
q[++r]=st;
dis[st]=L;
fa[st]=0;
for(int l=1;l<=r;l++){
int u=q[l];
d[++num]=dis[u];
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].to;
int w=edge[i].w;
if(vis[v]||v==fa[u])continue;
fa[v]=u;
dis[v]=dis[u]+w;
q[++r]=v;
}
}
ll ecnt=0;
sort(d+1,d+num+1);
int l1=1,r1=num;
while(l1<r1){
if(d[l1]+d[r1]<=k){
ecnt+=r1-l1;
l1++;
}else r1--;
}
return ecnt;
}
void solve(int u){
int g=calcg(u);
vis[g]=1;
ans+=calc(g,0);
for(int i=head[g];i;i=edge[i].nxt){
int v=edge[i].to;
int w=edge[i].w;
if(!vis[v])ans-=calc(v,w);
}
for(int i=head[g];i;i=edge[i].nxt){
int v=edge[i].to;
if(!vis[v])solve(v);
}
return;
}
int main(){
while(scanf("%d%d",&n,&k)!=EOF&&n+k){
cnt=ans=0;
memset(head,0,sizeof(head));
memset(vis,0,sizeof(vis));
for(int i=1;i<n;i++){
int u=read();
int v=read();
int w=read();
add(u,v,w);
add(v,u,w);
}
solve(1);
printf("%lld\n",ans);
}
return 0;
}

 

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