• BZOJ1468


    Description

    给出一棵n(n4×104)个节点的带边权树和k,求有多少距离不超过k的点对。

    Solution

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

    k=dst[u]+dst[v]ku,v(u,v)
    +k
    子树总距离不超过k的点对就是分治中的子问题,递归解决就好。对于另外那部分,我们设计函数cal(u)表示以u为根的树中满足dst[u]+dst[v]k的点对数(注意其中的dst是对于rtdst,而不是对于u的),则这部分的点对数=cal(rt)usonrtcal(u)cal(u)可以通过DFS出u的所有下属节点的dst再排序在O(nlogn)的时间复杂度内实现,具体可以看代码。

    时间复杂度为O(nlog2n)

    Code

    //Tree
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    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=4e4+10;
    int const INF=0x7FFFFFFF;
    int n,k;
    int cnt,h[N];
    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;
    }
    int ans;
    int fa[N],siz[N];
    bool vst[N];
    void dfs(int u)
    {
        siz[u]=1;
        for(int i=h[u];i;i=ed[i].nxt)
        {
            int v=ed[i].v;
            if(v!=fa[u]&&!vst[v]) fa[v]=u,dfs(v),siz[u]+=siz[v];
        }
    }
    int G,maxS,sumS;
    void getG(int u)
    {
        int mx=0;
        for(int i=h[u];i;i=ed[i].nxt)
        {
            int v=ed[i].v;
            if(v!=fa[u]&&!vst[v]) mx=max(mx,siz[v]),getG(v);
        }
        mx=max(mx,sumS-siz[u]);
        if(mx<maxS) maxS=mx,G=u;
    }
    int dst[N],tCnt,t[N];
    void getDst(int u)
    {
        t[++tCnt]=dst[u];
        for(int i=h[u];i;i=ed[i].nxt)
        {
            int v=ed[i].v;
            if(v!=fa[u]&&!vst[v]) dst[v]=dst[u]+ed[i].w,getDst(v);
        }
    }
    int cal(int u,int d0)
    {
        tCnt=0,dst[u]=0,getDst(u);
        sort(t+1,t+tCnt+1);
        int res=0,i=1,j=tCnt;
        while(i<j)
            if(t[i]+t[j]>k-d0-d0) j--;
            else res+=j-i,i++;
        return res;
    }
    void solve(int u)
    {
        vst[u]=true; fa[u]=0,dfs(u);
        ans+=cal(u,0);
        for(int i=h[u];i;i=ed[i].nxt)
        {
            int v=ed[i].v;
            if(vst[v]) continue;
            ans-=cal(v,ed[i].w);
            maxS=INF,sumS=siz[v],getG(v); solve(G);
        }
    }
    int main()
    {
        n=read();
        cnt=0; memset(h,0,sizeof h);
        for(int i=1;i<=n-1;i++)
        {
            int u=read(),v=read(),w=read();
            edAdd(u,v,w);
        }
        k=read();
        ans=0;
        fa[1]=0,dfs(1);
        maxS=INF,sumS=n,getG(1); solve(G);
        printf("%d",ans);
        return 0;
    }

    P.S.

    WA了两发居然是因为读入优化read()的返回值类型写成了char…吐血

  • 相关阅读:
    xyplorer设置备忘
    如何在CentOS 8上安装Python2 Python3
    为CentOS 8操作系统安装MySQL的方法,以安装MySQL 8为例
    SSH登录服务器报ECDSA host key "ip地址" for has changed and you have requested strict checking
    Linux常用命令大全
    转载:php的几种常用的数据交换格式
    转:GBK编码 VS UTF8编码
    转载:中文在UTF8和GBK编码中的范围
    转:SDL Specification and Description Language 简介
    转:Java中Split函数的用法技巧
  • 原文地址:https://www.cnblogs.com/VisJiao/p/8485743.html
Copyright © 2020-2023  润新知