• POJ 1741 Tree | 树分治


    求树上距离小于等于K的点对对数


    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define N 10005
    typedef long long ll;
    using namespace std;
    int n,K,fa[N],sze[N],son[N],dis[N],head[N];
    int ecnt;
    bool vis[N];
    ll ans;
    struct edge
    {
        int nxt,v,w;
    }e[2*N];
    void add(int u,int v,int w)//加边
    {
        e[++ecnt].v=v,e[ecnt].w=w,e[ecnt].nxt=head[u],head[u]=ecnt;
        e[++ecnt].v=u,e[ecnt].w=w,e[ecnt].nxt=head[v],head[v]=ecnt;
    }
    void init()//初始化
    {
        ans=ecnt=0;
        for (int i=1;i<=n;i++)
    	head[i]=vis[i]=0;
    }
    int calcG(int sv)//计算重心
    {
        static int qn,que[N];
        int u,v,mx=n,G; 
        que[qn=1]=sv,fa[sv]=0;
        for (int ql=1;ql<=qn;ql++)//以sv为根bfs求父子关系
        {
    	sze[u=que[ql]]=1,son[u]=0;
    	for (int i=head[u];i;i=e[i].nxt)
    	{
    	    if (vis[v=e[i].v] || v==fa[u]) continue;
    	    fa[v]=u,que[++qn]=v;
    	}
        }
        for (int ql=qn;ql>=1;ql--)//bfs的逆序更新每个节点的最大子树大小
        {
        //son数组表示每个节点为根最大子树大小
    	u=que[ql],v=fa[u];
    	if (qn-sze[u]>son[u]) son[u]=qn-sze[u];//如果他的子树比qn/2要小,就更新成qn-sze  
    	if (son[u]<mx) G=u,mx=son[u];//更新重心
    	if (!v) break;
    	sze[v]+=sze[u];//更新爸爸的子树
    	if (sze[u]>son[v]) son[v]=sze[u];
        }
        return G;
    }
    inline ll calc(int sv,int L)
    {
        static int qn,que[N],d[N];
        int u,v,d_n=0;
        que[qn=1]=sv,dis[sv]=L,fa[sv]=0;
        for (int ql=1;ql<=qn;ql++)
        {
    	d[d_n++]=dis[u=que[ql]];
    	for (int i=head[u];i;i=e[i].nxt)
    	{
    	    if (vis[v=e[i].v] || v==fa[u]) continue ;
    	    fa[v]=u,dis[v]=dis[u]+e[i].w,que[++qn]=v;
    	}
        }
        ll cnt=0;
        sort(d,d+d_n);
        int l=0,r=d_n-1;
        while (l<r)
        {
    	if (d[l]+d[r]<=K) cnt+=r-l++;
    	else --r;
        }
        return cnt;
    }
    void solve(int u)
    {
        int G=calcG(u);//求以u为根的子树的重心
        vis[G]=1;
        ans+=calc(G,0);//加上当前所在树中的答案
    	if (!vis[e[i].v]) ans-=calc(e[i].v,e[i].w);//减去每个子树的答案,因为子树的答案在子树中已经计算过了,不能重复计算
        for (int i=head[G];i;i=e[i].nxt)
    	if (!vis[e[i].v]) solve(e[i].v);//递归每个子树
    }
    int main()
    {
        while (scanf("%d%d",&n,&K),n || K)
        {
    	init();
    	for (int i=1;i<n;i++)
    	{
    	    int u,v,w;
    	    scanf("%d%d%d",&u,&v,&w);
    	    add(u,v,w);
    	}
    	solve(1);//以1为根
    	printf("%lld
    ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    ONNX 开始
    Linux 性能相关工具
    Prometheus + Grafana 快速上手
    RTSP 流相关工具介绍
    TensorFlow Serving
    TensorFlow 的 JupyterLab 环境
    【排序算法动画解】排序介绍及冒泡排序
    上课老师提问我什么是二叉查找树,我把这些动图拿了出来,动图图解及代码实现。
    如何找东西?查找算法之顺序查找和二分查找详解
    【数据结构和算法:简单方法】谈一谈优先队列的实现
  • 原文地址:https://www.cnblogs.com/mrsheep/p/8059110.html
Copyright © 2020-2023  润新知