zoukankan      html  css  js  c++  java
  • [Luogu P4178]Tree 题解(点分治+平衡树)

    题目大意

    给定一棵树,边带权,问有多少点对满足二者间距离$leq K$,$n leq 40000$.

    题解

    点分治专题首杀!$Jackpot!$

    (本来看着题意比较简单想捡个软柿子捏,结果手断了……)

    点分治的总结先鸽着,这里只说题解。

    分析一下题目:

    对于无根树上的某一节点x,如果把它看作根,树上的路径无非两类:

    1.经过x。

    2.不经过x,但在它的子树里。

    显然,后者利用点分治的思想经过递归处理可以转化为前者,那么我们就只需考虑第一类,

    这也是点分治的强大之处。

    我们设$dis[]$为节点到根节点x的距离,那我们要求的就是所有点对$(i,j)$,满足:

    $dis[i]+dis[j] leq K$,且$i,j$不位于同一子树内。

    接下来我们想办法实现对$(i,j)$的查询。

    我们可以先处理出一棵子树内的$dis[]$,然后对这颗子树进行操作:

    对于每个节点u,查询权值前缀$(K-dis[u])$即为所求,可以用类似于桶的树状数组实现。

    然后在桶中插入$dis[u]$,表示到跟距离为$dis[u]$的节点数增加1。

    按道理来讲,这玩意用树状数组很好搞对叭?

    但是我们要插入的权值是$dis[]$,这东西大起来可是完全可以撑爆数组下标的。

    (似乎有人拿树状数组水过了?打脸声啪啪啪啪)

    不过不要忘了还有一个大家耳熟能详的树锯结垢能实现插入权值、查询前缀和的操作:

    平衡树

    我们要查询的权值前缀和,不就是相当于查排名么?

    之后也就剩点分治的套路了,找根递归处理什么的。

    另外还有一些细节:

    不要照搬普通平衡树的板子,必须和实际应用相结合。我们这里的查排名可不一定是用平衡树里的权值查,所以有必要先插入再删除。

    而且,平衡树查的是不大于某个权值的数的量,不是查排名,所以getrank时没必要ans++。

    (相等的也要算啊!是$leq$不是$<$,所以答案return前要$+cnt[now]$)

    不仅如此,你先插入后查询会多一个,最后记得-1。

    还有!点分治和平衡树都会有$size[],root$之类的变量,如果设置很多不同的变量名会很麻烦而且容易错,

    这时候可以用C++的$namespace$,用法类似于结构体,只不过调用成员方式不是$.$而是$::$,

    这样变量名重复就没关系了。

    #define R
    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    const int N=80005;
    #ifdef R
    const int L=1<<20|1;
    char buffer[L],*S,*T;
    #define getchar() ((S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T))?EOF:*S++)
    #endif
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*f;
    }
    namespace Splay
    {
        int fa[N],cnt[N],son[N][3],size[N],key[N],type,root;
        void clear(int x)
        {
            fa[x]=cnt[x]=son[x][0]=son[x][1]=size[x]=key[x]=0;
        }
        void up(int x)
        {
            if(x)
            {
                size[x]=cnt[x];
                if(son[x][0])size[x]+=size[son[x][0]];
                if(son[x][1])size[x]+=size[son[x][1]];
            }
        }
        bool judge(int x)
        {
            return son[fa[x]][1]==x;
        }
        void rotate(int x)
        {
            int old=fa[x],oldf=fa[old],lr=judge(x);
            son[old][lr]=son[x][lr^1];
            fa[son[old][lr]]=old;
            son[x][lr^1]=old;
            fa[old]=x;
            fa[x]=oldf;
            if(oldf)son[oldf][son[oldf][1]==old]=x;
            up(old);up(x);
        }
        void splay(int x)
        {
            for(int f;f=fa[x];rotate(x))
                if(fa[f])rotate(judge(x)==judge(f)?f:x);
            root=x;
        }
        void ins(int x)
        {
            if(!root)
            {
                type++;
                key[type]=x;
                root=type;
                cnt[type]=size[type]=1;
                fa[type]=son[type][0]=son[type][1]=0;
                return ;
            }
            int now=root,f=0;
            while(1)
            {
                if(x==key[now])
                {
                    cnt[now]++;
                    up(now);up(f);
                    splay(now);
                    return ;
                }
                f=now;now=son[now][key[now]<x];
                if(!now)
                {
                    type++;
                    size[type]=cnt[type]=1;
                    son[type][0]=son[type][1]=0;
                    son[f][x>key[f]]=type;
                    fa[type]=f;
                    key[type]=x;
                    up(f);splay(type);
                    return ;
                }
            }
        }
        int getsum(int x)
        {
            int now=root,ans=0;
            while(1)
            {
                if(x<key[now])now=son[now][0];
                else
                {
                    ans+=size[son[now][0]];//puts("YOUSA");cout<<x<<' '<<now<<endl;
                    if(x==key[now])
                    {
                        ans+=cnt[now]-1;
                        splay(now);
                        return ans;
                    }
                    ans+=cnt[now];
                    now=son[now][1];
                }
            }
        }
        int pre()
        {
            int now=son[root][0];
            while(son[now][1])now=son[now][1];
            return now;
        }
        void del(int x)
        {
            getsum(x);
            if(cnt[root]>1)
            {
                cnt[root]--;
                up(root);
                return ;
            }
            if(!son[root][0]&&!son[root][1])
            {
                clear(root);root=0;
                return ;
            }
            if(!son[root][0])
            {
                int old=root;
                root=son[root][1];
                fa[root]=0;
                clear(old);
                return ;
            }
            else if(!son[root][1])
            {
                int old=root;
                root=son[root][0];
                fa[root]=0;
                clear(old);
                return ;
            }
            int old=root,L=pre();
            splay(L);
            son[root][1]=son[old][1];
            fa[son[old][1]]=root;
            clear(old);
            up(root);
        }
    }
    int n,tot,head[N],len[N<<1],nxt[N<<1],to[N<<1],K;
    int size[N],vis[N],maxx,root,sz,ans,dis[N],st[N],top;
    void add(int x,int y,int z)
    {
        to[++tot]=y;
        nxt[tot]=head[x];
        len[tot]=z;
        head[x]=tot;
    }
    void cacl(int x,int fa)
    {
        st[++top]=dis[x];
        Splay::ins(K-dis[x]);
        ans+=Splay::getsum(K-dis[x]);
        Splay::del(K-dis[x]);
        for(int i=head[x];i;i=nxt[i])
            if(!vis[to[i]]&&to[i]!=fa)cacl(to[i],x);
    }
    void dfs(int x,int fa)
    {
        for(int i=head[x];i;i=nxt[i])
        {
            int y=to[i];
            if(vis[y]||y==fa)continue;
            dis[y]=dis[x]+len[i];
            dfs(y,x);
        }
    }
    inline void groot(int x,int fa)
    {
        size[x]=1;
        int num=0;
        for(int i=head[x];i;i=nxt[i])
        {
            int y=to[i];
            if(y==fa||vis[y])continue;
            groot(y,x);
            size[x]+=size[y];
            num=max(num,size[y]);
        }
        num=max(num,sz-size[x]);
        if(maxx>num)maxx=num,root=x;
    }
    void dac(int x)
    {
        maxx=0x3f3f3f3f,root=0;
        groot(x,0);
        x=root;dis[x]=0;vis[x]=1;top=0;Splay::root=Splay::type=0;
        dfs(x,0);
        if(head[x])Splay::ins(0);
        for(int i=head[x];i;i=nxt[i])
        {
            int y=to[i];
            if(vis[y])continue;
            cacl(y,x);
            if(nxt[i])while(top)Splay::ins(st[top--]);
        }
        for(int i=head[x];i;i=nxt[i])
        {
            int y=to[i];
            if(vis[y])continue;
            sz=size[y];
            dac(y);
        }
    }
    int main()
    {
        n=read();
        for(int i=1;i<n;i++)
        {
            int x=read(),y=read(),z=read();
            add(x,y,z);add(y,x,z);
        }
        K=read();sz=n;
        dac(1);
        cout<<ans<<endl;
        return 0;
    }
    虽然很长但极其无脑
  • 相关阅读:
    【贪心】【堆】Gym
    【并查集】Gym
    【拓扑排序】【bitset】Gym
    【递归】【线段树】【堆】AtCoder Regular Contest 080 E
    【二分图】【并查集】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem L. Canonical duel
    【动态规划】【滚动数组】【bitset】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem J. Terminal
    【二分】【字符串哈希】【二分图最大匹配】【最大流】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem I. Minimum Prefix
    【枚举】【最小表示法】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem F. Matrix Game
    【推导】【构造】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem E. Space Tourists
    【推导】【贪心】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem D. Clones and Treasures
  • 原文地址:https://www.cnblogs.com/Rorschach-XR/p/11252682.html
Copyright © 2011-2022 走看看