zoukankan      html  css  js  c++  java
  • 5814. 【NOIP提高A组模拟2018.8.14】 树

    ###Description
    梦游中的你来到了一棵 N 个节点的树上. 你一共做了 Q 个梦, 每个梦需要你从点 u 走到 点 v 之后才能苏醒, 由于你正在梦游, 所以每到一个节点后,你会在它连出去的边中等概率地 选择一条走过去, 为了确保第二天能够准时到校, 你要求出每个梦期望经过多少条边才能苏 醒. 为了避免精度误差, 你要输出答案模10^9 + 7的结果.

    ###Input
    第一行两个整数分别代表 N 和 Q. 接下来 N-1 行, 每行两个整数 u, v 代表树中的一条边. 接下来 Q 行, 每行两个整数代表询问的 u,v.

    ###Output
    一共 Q 行, 每行一个整数代表答案

    ###Sample Input
    4 2
    1 2
    2 3
    3 4
    1 4
    3 4

    ###Sample Output
    9
    5

    ###Data Constraint
    对于 20%的数据, N <= 10.
    对于 40%的数据, N <= 1000.
    另有 20%的数据, 保证给定的树是一条链.
    对于 100%的数据, N <= 100000, Q <= 100000.

    ###Hint
    这里写图片描述

    ###题解:
    首先,我们仔细看题。
    仔细看,再仔细看。
    然而,你没有熟练掌握一些概率或期望的知识,真的很难解释题意。

    一般期望题的样例解释大概都是这样:有1/a概率1步到达,有1/b概率3步到达,有1/c概率5步到达……我们可以计算出期望d步到达。

    我们看看这段东西,是某栋爷的解释。
    然后再加上期望的定义:花费*概率
    这样子自己手玩玩不就可以比较好地推出样例了吗?

    但是,有一个问题,一般期望不是实数吗?
    这个你先不管它,因为出题人认为做题人太强了,一眼就看到了正解。
    所以,数据就变这样了。

    不多说废话。(已经再解释期望时说了很多了)

    我们看题——由于是从x走到y。
    那么必然有一个lca。然后行走路径变成x→lca。lca→y。
    变成一上一下。
    那么我们就维护两个东西:
    f[i]表示当前点i走到它父亲节点的期望。
    g[i]表示当前点i从它父亲走回来的期望。

    f就很好转移:
    f[v]:=1deg[v]+Σ(ison[v])f[i]+f[v]+1deg[v]f[v]:=dfrac {1}{deg[v]}+Sigma(iin son[v]) dfrac{f[i]+f[v]+1}{deg[v]}
    deg表示当前点的出度。
    那么期望即为:花费* 概率= 1 *(1/dep[v])
    上面的式子表示当前到v这个点。
    走到父亲的期望+
    返回v的儿子后再走回来v这个点再走到父亲的期望。
    很好看,对吧?
    我们可以考虑化简:两边同乘deg[v],移一下项,即可得到:
    f[v]:=deg[v]+Σ(ison[v])f[i]f[v]:=deg[v]+Sigma(iin son[v]) f[i]

    再看看g。g就有点麻烦:
    g[v]:=1deg[fa]+g[v]+g[fa]+1deg[fa]+Σ(ison[fa]i&lt;&gt;v)f[i]+g[v]+1deg[fa]g[v]:=dfrac{1}{deg[fa]}+dfrac{g[v]+g[fa]+1}{deg[fa]}+Sigma(iin son[fa]且i&lt;&gt;v) dfrac{f[i]+g[v]+1}{deg[fa]}
    fa表示v的父亲
    表示为当前到fa这个点。
    直接走到v的期望+
    走到fa的父节点后返回再走到fa再走到v的期望+
    走到fa的非v的子节点再返回fa再走到v的期望。
    同样地考虑简化。(自己推)
    得到:
    g[v]:=g[fa]+deg[fa]+Σ(ison[fa])f[i]g[v]:=g[fa]+deg[fa]+Sigma(iin son[fa]) f[i]

    这两个式子已经很优秀了。
    而且同时解决了上面为什么期望值是整数的问题。
    这里写图片描述
    答案是可以的。
    我们分析式子f先。
    我们画一棵树。
    这里写图片描述
    明显,对于叶子节点的f就为1(deg)
    那么我们可以看做是叶子节点计算了一次连接它的边:
    (为了方便表示,红色边为表示被计算了一次)
    这里写图片描述
    然后我们看看黄色的点。
    那么这个黄色的点的f就为5(f[son]=2,deg=5)
    然后同样地可以用上面的方法来看:
    这里写图片描述
    (绿色表示计算了两次)
    于是,黄色的点计算完之后图长这样。
    然后蓝色的点的f为11(f[son]=7,deg=4)
    依然覆盖
    最后就会变成满屏的绿
    这里写图片描述
    于是,我们就可以发现,f[x]的式子即表示为:
    f[x]:=siz[x]21f[x]:=siz[x]*2-1
    siz为当前点为根的子树大小
    然后,同样的,g也可以用上面的思想来算。
    注意,f是针对子树的,那么g则是针对除了它的子树的计算方法。
    那么g[x]的式子即表示为:
    g[x]:=(nsiz[x])21g[x]:=(n-siz[x])*2-1

    这就灰常地优美了。
    然后,我们求出一个siz,然后直接跑一遍,求出f与g。
    然后,由于我们是求x→lca。lca→y
    那么每次就把f[x]、g[x]加上他们的父亲即可。

    由于本人比较蒟,只好打一个树链剖分的求lca。然而实验证明,在随机数据的情况下,这种方法比树上rmq更优。
    ###代码

    uses math;
    type
            new=record
                    f,g:int64;
            end;
    var
            tot,i,j,k,l,n,m,x,y,ans,answer,gs,a,b,nx,ny,lca:longint;
            tov,next,last,size,f,g,father,dep,son,siz,tree,pre,top,fa:array[0..300000] of int64;
            ff,gg:array[1..100000,1..3] of int64;
            tr:array[1..400000] of new;
    procedure insert(x,y:longint);
    begin
            inc(tot);
            tov[tot]:=y;
            next[tot]:=last[x];
            last[x]:=tot;
    end;
    procedure dfsf(v,fa:longint);
    var
            i,j,k,l:longint;
    begin
            father[v]:=fa;
            i:=last[v];
            f[v]:=2*siz[v]-1;
            g[v]:=2*(n-siz[v])-1;
            if v<>1 then
            begin
                    f[v]:=f[v]+f[fa];
                    g[v]:=g[v]+g[fa];
            end;
            while i<>0 do
            begin
                    if tov[i]<>fa then
                    begin
                            dfsf(tov[i],v);
                    end;
                    i:=next[i];
            end;
    end;
    procedure dfsfd(x,f,d:longint);
    var
            i,j,k,l:longint;
    begin
            fa[x]:=f;
            dep[x]:=d;
            siz[x]:=1;
            i:=last[x];
            while i<>0 do
            begin
                    if tov[i]<>fa[x] then
                    begin
                            dfsfd(tov[i],x,d+1);
                            siz[x]:=siz[x]+siz[tov[i]];
                            if (son[x]=0) or (siz[tov[i]]>siz[son[x]]) then son[x]:=tov[i];
                    end;
                    i:=next[i];
            end;
    end;
    procedure dfs(v,num:longint);
    var
            i,j,k,l:longint;
    begin
            inc(gs);
            tree[v]:=gs;
            top[v]:=num;
            pre[gs]:=v;
            if son[v]=0 then exit;
            dfs(son[v],num);
            i:=last[v];
            while i<>0 do
            begin
                    if tov[i]<>fa[v] then
                    begin
                            if tov[i]<>son[v] then
                            begin
                                    dfs(tov[i],tov[i]);
                            end;
                    end;
                    i:=next[i];
            end;
    end;
    function getans(x,y:longint):longint;
    var
            i,j,tx,ty,k:longint;
    begin
            tx:=top[x];
            ty:=top[y];
            while tx<>ty do
            begin
                    if dep[tx]<dep[ty] then
                    begin
                            y:=fa[ty];
                            ty:=top[y];
                    end
                    else
                    begin
                            x:=fa[tx];
                            tx:=top[x];
                    end;
            end;
            if dep[x]>dep[y] then exit(y)
            else exit(x);
    end;
    begin
            assign(input,'tree.in');reset(input);
            assign(output,'tree.out');rewrite(output);
            readln(n,m);
            for i:=1 to n-1 do
            begin
                    readln(x,y);
                    insert(x,y);
                    insert(y,x);
                    inc(size[x]);
                    inc(size[y]);
            end;  
            dfsfd(1,0,1);
            dfs(1,1);
            f[1]:=-1;
            g[1]:=-1;
            dfsf(1,0);
            for i:=1 to m do
            begin
                    readln(a,b);
                    lca:=getans(a,b);
                    writeln((f[a]-f[lca]+g[b]-g[lca]) mod 1000000007);
            end;
    end.
    
    我活在这夜里。无论周围多么黑暗,我都要努力发光!我相信着,终有一天,我会在这深邃的夜里,造就一道最美的彩虹。
  • 相关阅读:
    C# WebBrowser屏蔽alert的方法
    C# webbrowser实现真正意义上的F5刷新
    用于验证码图片识别的类(C#源码)
    递归
    排列组合数
    八皇后问题
    算24
    素数环
    acm
    qsort
  • 原文地址:https://www.cnblogs.com/RainbowCrown/p/11148398.html
Copyright © 2011-2022 走看看