• [日常摸鱼]poj1741Tree-点分治


    还有两天就要去FJWC啦…

    题意:一颗无根树,$k$为给定常数,求树上距离不超过$k$的点对的数量,多组数据,$n leq 10^4$.


    应该是点分治经典题~

    一般对于无根树我们都可以把它转变成有根树(其实树上路径不管哪个根都一样嘛),假设我们已经钦定了一个根$rot$(后面会说其实这个根应该是重心),对于$rot$这颗树中的对答案有贡献的路径,要么经过$rot$,要么直接就是$rot$的某一颗子树的答案,这两种情况之间没有交集直接相加,这里就出现了子问题。

    而对于经过$rot$的路径的贡献,对可以通过处理一遍$rot$的子树里到它的距离$dis[]$在$O(nlogn)$时间内统计(具体见代码,其实就排个序然后扫描)。这样对于每个点我们只要找根,处理经过$rot$的路径的答案(1),删除$rot$然后递归对$rot$的所有相邻点$cur$递归的求答案(2),不过这里出现了重复计算(设$w$为当前点到相邻点$cur$的边的长度):在(1)当中其实重复计算了$cur$里满足$dis[x]+w+dis[y]+w leq k$的点对多算了,所以我们还要减掉这种答案(你问我怎么减?看代码啦~)

    实现删除的时候有个trick就是用一个数组$vis[]$来记录这个点处理过没…处理过了直接不管它。

    时间复杂度?假设最多递归了$T$层,计算每一层的答案一定不超过$O(nlogn)$,总复杂度就变成了$O(T*nlogn)$。如果随便钦定某个根递归层数太大的话会被卡成$O(n^2logn)$的,嗯那么要让递归层数小的话当然就选择这个子树的重心啦~每次$O(n)$的算重心,这样$x$的所有子树的大小都不会超过整棵树大小的一半,所以深度就是$(logn)$的了,总复杂度$O(nlog^2 n)$~

    呼~

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define rep(i,n) for(register int i=1;i<=n;i++)
    using namespace std;
    const int N=10005;
    inline int read()
    {
        int s=0,f=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=0;c=getchar();}
        while(c>='0'&&c<='9'){s=s*10+c-'0';c=getchar();}
        return f?s:-s;
    }
    struct edge
    {
        int to,nxt,w;
        edge(int to=0,int nxt=0,int w=0):to(to),nxt(nxt),w(w){}
    }edges[N<<1];
    int n,k,cnt,tot;
    int head[N<<1],dis[N],size[N],g[N],vis[N];
    
    inline void addEdge(int u,int v,int w)
    {
        edges[++cnt]=edge(v,head[u],w);
        head[u]=cnt;
    }
    #define cur edges[i].to
    inline void dfs_root(int x,int f)
    {
        size[x]=1;g[x]=0;
        for(register int i=head[x];i;i=edges[i].nxt)if(!vis[cur]&&cur!=f)
        {
            dfs_root(cur,x);size[x]+=size[cur];
            g[x]=max(g[x],size[cur]);
        }
    }
    inline int calc_root(int x,int f,int rot)
    {
        int res=0,minx=n;
        for(register int i=head[x];i;i=edges[i].nxt)if(!vis[cur]&&cur!=f)
        {
            int tmp=calc_root(cur,x,rot);
            if(g[tmp]<minx)minx=g[tmp],res=tmp;
        }
        g[x]=max(g[x],size[rot]-size[x]);
        if(g[x]<minx)minx=g[x],res=x;
        return res;
    }
    inline int get_root(int x)
    {
        dfs_root(x,-1);
        return calc_root(x,-1,x);
    }
    inline void dfs_dis(int x,int d,int f)
    {
        dis[++tot]=d;
        for(register int i=head[x];i;i=edges[i].nxt)
            if(!vis[cur]&&cur!=f)dfs_dis(cur,d+edges[i].w,x);
    }
    inline int calc(int x,int w)
    {
        tot=0;
        dfs_dis(x,w,-1);sort(dis+1,dis+tot+1);
        int j=tot,res=0;
        for(register int i=1;i<=j;i++)
        {
            while(i<j&&dis[i]+dis[j]>k)j--;
            res+=j-i;
        }return res;
    }
    inline int dfs(int x)
    {
        int rot=get_root(x),res=0;
        vis[rot]=1;
        res+=calc(rot,0);
        for(register int i=head[rot];i;i=edges[i].nxt)if(!vis[cur])
        {
            res-=calc(cur,edges[i].w);//算距离的时候多加个变量就行啦
            //注意不要把两种合起来写…一个是rot的子树一个是cur的子树 
            res+=dfs(cur);
        }
        return res;
    }
    
    #undef cur
    int main()
    {
        while(scanf("%d%d",&n,&k)==2&&(n+k))
        {
            cnt=0;
            rep(i,n-1)
            {
                int u,v,w;u=read();v=read();w=read();
                addEdge(u,v,w);addEdge(v,u,w);
            }
            printf("%d
    ",dfs(1));
            rep(i,cnt)head[i]=0;rep(i,n)vis[i]=0;
        }
        return 0;
    }
  • 相关阅读:
    node.js_1
    CSS实现垂直居中的方法
    Javascript实现图片的预加载的完整实现------》转载自(空城计-Code记)
    面向对象----选项卡
    for循环执行步骤
    适配器模式 在Android中的简单理解
    单例模式
    Android的ImageLoader图片加载简单逻辑
    在魅族手机上使用ObjectAnimator竟然不兼容?原来是这样……
    Android异步消息处理机制,Handler,Message,Looper的简单理解
  • 原文地址:https://www.cnblogs.com/yoshinow2001/p/8418246.html
Copyright © 2020-2023  润新知