M_sea

洛谷4178 Tree
题目描述给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K。传送门算法点分治裸题。考虑...
扫描右侧二维码阅读全文
16
2018/08

洛谷4178 Tree

题目描述

给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K。

传送门

算法

点分治裸题。

考虑暴力做法。在【模板】点分治1的基础上从1到k循环统计一遍答案即可。

考虑对暴力做优化。发现算法的主要瓶颈在calc函数内,我们考虑把它从$O(n^2)$降至$O(n\ \log\ n)$。

我们先把$o$数组排个序,然后设置两个指针从两端往中间靠拢,并且统计出答案即可。

细节见代码。

代码

#include <bits/stdc++.h>
#define re register
using namespace std;

inline int read() {
    int X=0,w=1; char c=getchar();
    while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
    while (c>='0'&&c<='9') X=(X<<3)+(X<<1)+c-'0',c=getchar();
    return X*w;
}

int n,k;

//邻接表 
struct Edge { int v,w,nxt; } e[80010];
int head[40010],cnt=0;

inline void addEdge(int u,int v,int w) {
    e[++cnt].v=v; e[cnt].w=w;
    e[cnt].nxt=head[u]; head[u]=cnt;
}

//求重心 
int root,sum;
int f[40010],sz[40010];
bool vis[40010];

inline void getroot(int u,int fa) {
    sz[u]=1; f[u]=0;
    for (re int i=head[u];i;i=e[i].nxt) {
        int v=e[i].v;
        if (v==fa||vis[v]) continue;
        getroot(v,u); sz[u]+=sz[v];
        f[u]=max(f[u],sz[v]);
    }
    f[u]=max(f[u],sum-sz[u]);
    if (f[u]<f[root]) root=u;
}

int o[40010],dep[40010];

//求深度 
inline void getdeep(int u,int fa) {
    o[++cnt]=dep[u];
    for (re int i=head[u];i;i=e[i].nxt) {
        int v=e[i].v,w=e[i].w;
        if (v==fa||vis[v]) continue;
        dep[v]=dep[u]+w;
        getdeep(v,u);
    }
}

int ans;

//点分治
inline int calc(int u,int d0) {
    cnt=0; dep[u]=d0; getdeep(u,0);
    sort(o+1,o+cnt+1);
    int i=1,j=cnt,rt=0;
    while (i<j) {
        if (o[i]+o[j]<=k) rt+=j-i,i++;
        else j--;
    }
    return rt;
}
inline void solve(int u) {
    ans+=calc(u,0); vis[u]=1;
    for (re int i=head[u];i;i=e[i].nxt) {
        int v=e[i].v,w=e[i].w;
        if (vis[v]) continue;
        ans-=calc(v,w);
        sum=sz[v],root=0;
        getroot(v,0);
        solve(root);
    }
}

int main() {
    n=read();
    for (re int i=1;i<n;i++) {
        int a=read(),b=read(),c=read();
        addEdge(a,b,c);
        addEdge(b,a,c);
    }
    k=read();
    sum=f[0]=n;
    getroot(1,0);
    solve(root);
    printf("%d\n",ans);
    return 0;
}
最后修改:2018 年 11 月 09 日 05 : 14 PM

发表评论