• 洛谷4178 BZOJ1468 Tree题解点分治


    点分治的入门练习。

    题目链接 

    BZOJ的链接(权限题)

    关于点分治的思想我就不再重复了,这里重点说一下如何判重。

    我们来看上图,假设我们去除了1节点,求出d[2]=1,d[3]=d[4]=2

    假设k为5,这样我们会认为节点(2,3)(2,4)(3,4)的距离小于k,从而累计到答案中

    但是我们以2为root做点分治时还会将(3,4)计算一遍,这样就重复了

    所以我们每一次计算答案时还要讲所有多余情况减去,最终答案才是我们要求的答案

    不难发现多余情况是在root节点与root子节点重复统计的,我们在点分治时将所有root子节点的答案减去就好了

    # include<iostream>
    # include<cstdio>
    # include<algorithm>
    # include<set>
    # include<cmath>
    using namespace std;
    const int mn = 40005;
    struct edge{int to,next,dis;};
    edge e[mn*2];
    int head[mn],edge_max;
    void add(int x,int y,int z)
    {
        e[++edge_max].to=y;
        e[edge_max].dis=z;
        e[edge_max].next=head[x];
        head[x]=edge_max;
    }
    bool vis[mn];
    int n,k,ans;
    int mx[mn],siz[mn],root,sum;
    int d[mn],deep[mn];
    void getroot(int x,int fa)
    {
        siz[x]=1,mx[x]=0;
        for(int i=head[x];i;i=e[i].next)
        {
            int u=e[i].to;
            if(u==fa || vis[u])
                continue;
            getroot(u,x);
            siz[x]+=siz[u];
            mx[x]=max(mx[x],siz[u]);
        }
        mx[x]=max(mx[x],sum-siz[x]);
        if(mx[root]>mx[x])
            root=x;
    }
    void getdeep(int x,int fa)
    {
        deep[++deep[0]]=d[x];
        for(int i=head[x];i;i=e[i].next)
        {
            if(e[i].to==fa || vis[e[i].to])
                continue;
            d[e[i].to]=d[x]+e[i].dis;
            getdeep(e[i].to,x);
        }
    }
    int cal(int x,int now)
    {
        d[x]=now,deep[0]=0;
        getdeep(x,0);
        sort(deep+1,deep+1+deep[0]);
        int t=0,l,r;
        for(int l=1,r=deep[0];l<r;)
        {
            if(deep[l]+deep[r]<=k)
            {
                t+=r-l;
                l++;
            }
            else r--;
        }
        return t;
    }
    void solve(int x)
    {
        vis[x]=1;
        ans+=cal(x,0);
        for(int i=head[x];i;i=e[i].next)
        {
              if(vis[e[i].to])
                continue;
              ans-=cal(e[i].to,e[i].dis);
              sum=siz[e[i].to];
              root=0;
              getroot(e[i].to,root);
              solve(root);
        }
    }
    int main()
    {
        int x,y,z;
        scanf("%d",&n);
        for(int i=1;i<n;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            add(x,y,z),add(y,x,z);
        }
        scanf("%d",&k);
        mx[0]=1<<30,sum=n;
        getroot(1,0);
        solve(root);
        printf("%d
    ",ans);
        return 0;
    }

     

  • 相关阅读:
    CXF入门案例
    计算python内部数据结构时间效率-源代码
    笨办法学习python-ex41源码加自己注释
    python之random模块
    python之模块、类、对象
    购物车代码
    ql的python学习之路-day1
    数组转置(函数指针,回调函数)
    将一句话按单词逆转
    *一个二级指针的练习(输入一个数,输出对应的月份)
  • 原文地址:https://www.cnblogs.com/logeadd/p/9003258.html
Copyright © 2020-2023  润新知