BZOJ1468 - Tree

Portal

Description

给出一棵\(n(n\leq4\times10^4)\)个点的带边权的树,求有多少对点间的距离不超过\(k\)

Solution

点分治裸题。
点分治是树分治的一种。分治(Divide and Conquer),也就是分而治之。具体来说就是将一个问题划分为若干个规模更小的同样的问题直到子问题可以简单求解,并将这些子问题的结果进行合并。在树的分治上,合并的就是子树的结果啦。
首先我们要找到树的重心,然后将重心作为根。树的重心是树中的一个点,其所有的子树中最大的子树节点数最少。这样可以让这棵树尽可能平衡,而且其最大的子树大小不超过\(n/2\)——因此分治的层数是\(O(logn)\)
记树的根节点为\(rt\),树中节点\(u\)\(rt\)的距离为\(dst[u]\),则有:

树中距离不超过\(k\)的点对数等于满足\(dst[u]+dst[v]≤k\)\(u,v\)不属于同一子树的点对\((u,v)\)
,加上子树中距离不超过\(k\)的点对数。

子树总距离不超过\(k\)的点对就是分治中的子问题,递归解决就好。对于另外那部分,我们设计函数calc(u)表示以\(u\)为根的树中满足\(dst[v_1]+dst[v_2]≤k\)的点对数(注意其中的\(dst\)是对于\(rt\)\(dst\),而不是对于\(u\)的),则这部分的点对数=\(calc(rt)−∑_{u\in son_{rt}}calc(u)\)calc(u)可以通过DFS出u的所有下属节点的\(dst\)再排序在\(O(nlogn)\)的时间复杂度内实现。

Code

//Tree
#include <algorithm>
#include <cstdio>
using std::max; using std::sort;
typedef long long lint;
inline char gc()
{
    static char now[1<<16],*s,*t;
    if(s==t) {t=(s=now)+fread(now,1,1<<16,stdin); if(s==t) return EOF;}
    return *s++;
}
inline int read()
{
    int x=0; char ch=gc();
    while(ch<'0'||'9'<ch) ch=gc();
    while('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x;
}
int const N=5e4+10;
int n,k;
int h[N],cnt;
struct edge{int v,w,nxt;} ed[N<<1];
void edAdd(int u,int v,int w)
{
    cnt++; ed[cnt].v=v,ed[cnt].w=w,ed[cnt].nxt=h[u],h[u]=cnt;
    cnt++; ed[cnt].v=u,ed[cnt].w=w,ed[cnt].nxt=h[v],h[v]=cnt;
}
lint ans;
int G,siz0,fa[N],siz[N],chSiz[N]; bool vst[N];
void getG(int u,int fa)
{
    siz[u]=1,chSiz[u]=0;
    for(int i=h[u];i;i=ed[i].nxt)
    {
        int v=ed[i].v,w=ed[i].w;
        if(vst[v]||v==fa) continue;
        getG(v,u);
        siz[u]+=siz[v],chSiz[u]=max(chSiz[u],siz[v]);
    }
    chSiz[u]=max(chSiz[u],siz0-siz[u]);
    if(chSiz[u]<chSiz[G]) G=u;
}
int t[N],tCnt; 
void getDst(int u,int fa,int d)
{
    t[++tCnt]=d;
    for(int i=h[u];i;i=ed[i].nxt)
    {
        int v=ed[i].v,w=ed[i].w;
        if(vst[v]||v==fa) continue;
        getDst(v,u,d+w);
    }
}
int calc(int u,int d0)
{
    tCnt=0; getDst(u,0,d0);
    sort(t+1,t+tCnt+1);
    lint res=0;
    for(int i=1,j=tCnt;i<j;i++)
    {
        while(t[i]+t[j]>k) j--;
        if(i<j) res+=j-i;
    }
    return res;
}
void solve(int u);
void DC(int u)
{
    vst[u]=true; ans+=calc(G,0);
    for(int i=h[u];i;i=ed[i].nxt)
    {
        int v=ed[i].v;
        if(vst[v]) continue;
        if(siz[v]>siz[u]) siz[v]=siz0-siz[u];
        ans-=calc(v,ed[i].w);
    }
    for(int i=h[u];i;i=ed[i].nxt) {int v=ed[i].v; if(!vst[v]) solve(v);}
}
void solve(int u) {siz0=siz[u],G=0,chSiz[G]=n,getG(u,0),DC(G);}
int main()
{
    n=read();
    for(int i=1;i<=n-1;i++)
    {
        int u=read(),v=read(),w=read();
        edAdd(u,v,w);
    }
    k=read();
    ans=0; siz[1]=n,solve(1);
    printf("%lld\n",ans);
    return 0;
}

P.S.

这题以前就写过,题解基本是复制我以前的题解。
%%%elijahqi,助我理解点分治

posted @ 2018-04-02 20:27  VisJiao  阅读(109)  评论(0编辑  收藏  举报