zoukankan      html  css  js  c++  java
  • [2016北京集训试题7]thr-[树形dp+树链剖分+启发式合并]

    Description

    Solution

    神仙操作orz。

    首先看数据范围,显然不可能是O(n2)的。(即绝对不是枚举那么简单的),我们考虑dp。

    定义f(x,k)为以x为根的子树中与x距离为k的节点数;g(x,k)为在以x为根的子树中选择两个点,使得另一个点应在x子树外且离x距离为k的方案数(或者距离为0)。但是这样子暴力转移怕是会崩em,考虑优化。

    这里的树是棵静态树,考虑树链剖分,点分治之类的思想。

    最后,由于很多时候它的复杂度和树的高度有关,考虑长链剖分。

    转移的话,暴力枚举所有轻链(啊我可能说的不是很清楚,类似启发式合并的想法吧)。

    那么该点对应的长链怎么办呢?类似树剖的重儿子,我们定义节点x的长儿子为son[x],它是只有一个的。

    而假如该点只有一个儿子,f(x,k)=f(son[x],k-1),g(x,k)=g(son[x],k+1)。

    还是和树剖一样的思想,我们把同一条长链的信息存在一起,例如把f(son[x],k-1)的信息与f(x,k)存在同一个地址下。(我们能这么做是因为son[x]的信息在被x更新完后它就没用了啊)。g的话同理。

    Code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    const int N=50010;
    int mxdep[N],top[N],fa[N],dep[N],son[N],cnt_f[N],cnt_g[N],cntf=0,cntg=0;
    ll F[N],G[N<<1];
    int getlen(int x){return mxdep[x]-dep[x]+1;}
    ll &f(int x,int y){return F[cnt_f[top[x]]+dep[x]-dep[top[x]]+y];}
    ll &g(int x,int y){return G[cnt_g[top[x]]-dep[x]+dep[top[x]]+y];}
    struct _pas{int y,nxt;}_g[N<<1];int h[N],tot=0;
    void dfs1(int x,int f)
    {
        fa[x]=f;dep[x]=dep[f]+1;mxdep[x]=dep[x];son[x]=0;
        for (int i=h[x];i;i=_g[i].nxt)
        if (_g[i].y!=f){
            dfs1(_g[i].y,x);
            mxdep[x]=max(mxdep[x],mxdep[_g[i].y]);
            if (mxdep[son[x]]<mxdep[_g[i].y]) son[x]=_g[i].y;
        }
    }
    void dfs2(int x)
    {
        top[x]=son[fa[x]]==x?top[fa[x]]:x;
        if (top[x]==x)
        {
            int d=getlen(x);
            cnt_f[x]=cntf;cnt_g[x]=cntg+d;
            cntf+=d;cntg+=2*d;
        }
        if (son[x]) dfs2(son[x]);
        for (int i=h[x];i;i=_g[i].nxt)
        if (_g[i].y!=son[x]&&_g[i].y!=fa[x]) dfs2(_g[i].y);
    }
    ll ans;
    void dp(int u)
    {
        f(u,0)=1;
        if (son[u]) dp(son[u]),ans+=g(u,0);
        for (int i=h[u];i;i=_g[i].nxt)
        if (_g[i].y!=son[u]&&_g[i].y!=fa[u]) 
        {
            int v=_g[i].y;dp(v);int len=getlen(v);
            for (int i=1;i<=len;i++) ans+=g(u,i)*f(v,i-1);
            for (int i=1;i<len;i++) ans+=g(v,i)*f(u,i-1);
            for (int i=1;i<=len;i++) g(u,i)+=f(u,i)*f(v,i-1);
            for (int i=1;i<=len-2;i++) g(u,i)+=g(v,i+1);
            for (int i=1;i<=len;i++) f(u,i)+=f(v,i-1);
        }
    }
    void _clear()
    {
        tot=cntf=cntg=0;
        memset(h,0,sizeof(h));
        memset(F,0,sizeof(F));
        memset(G,0,sizeof(G));
    }
    int n,x,y;
    int main()
    {
        while (1)
        {
            scanf("%d",&n);
            if (!n) return 0;
            _clear();
            for (int i=1;i<n;i++)
            {
                scanf("%d%d",&x,&y);
                _g[++tot]=_pas{y,h[x]};h[x]=tot;
                _g[++tot]=_pas{x,h[y]};h[y]=tot;
            }
            ans=0;
            dfs1(1,0);dfs2(1);dp(1);
            printf("%lld
    ",ans);       
        }
    }
  • 相关阅读:
    docker学习
    redis哨兵部署
    HUE中一些重要元数据表的DDL整理
    Autosys中ON_HOLD和ON_ICE的区别
    Spark结构化API的执行过程——Logical Plan & Physical Plan
    关于Spark中Columns的引用方法
    关于Spark Dataset API中的Typed transformations和Untyped transformations
    关于Kafka Consumer 与 Partitions
    使用sed根据变量值注释掉文件中相匹配的记录行
    sqoop export to teradata时出现java.lang.NullPointerException
  • 原文地址:https://www.cnblogs.com/coco-night/p/9615465.html
Copyright © 2011-2022 走看看