• Luogu2081 「NOI2012」 迷失游乐园


    Description

    在一棵基环树(或者树)任选点,不能走走过的点,求走到不能走的期望长度

    (nle 10^5),环上的点 (le 20)

    Solution

    树的部分可以考虑用换根法来解决

    定义 (f_i) 为以 (i) 为起点,在子树里面的期望路径长度

    这里有一个小误区:这个定义转移的时候应该除以 (du_i-1) (状态定义)

    正确性用期望线性性能给

    另一个部分用换根做就行

    struct task1{
        int n, m, cnt=1, du[N], head[N];
        double f[N], g[N], ans, d[N];
        struct node {
            int to, dis, nxt;
        } e[N << 1];
        inline void add(int u, int v, int w) {
            e[++cnt].to = v;
            ++du[v];
            e[cnt].nxt = head[u];
            e[cnt].dis = w;
            return head[u] = cnt, void();
        }
        inline void dfs1(int x, int fa) {
        for (reg int i = head[x]; i; i = e[i].nxt) {
            int t = e[i].to;
            if (t == fa)
                continue;
            dfs1(t, x);
            d[x] += f[t] + e[i].dis;
        } f[x] = d[x] / max(1,du[x]-1);
        return;
        }
        inline void dfs2(int x, int fa) {
            for (reg int i = head[x]; i; i = e[i].nxt) {
                int t = e[i].to;
                if (t == fa)
                    continue;
                d[t] += (d[x] - f[t] - e[i].dis) / max(1ll, du[x] - 1) + e[i].dis;
                dfs2(t, x);
            }
            return;
        }
        inline void main()
        {
            for(reg int i=1,u,v,w;i<=m;++i) u = read(), v = read(), w = read(), add(u, v, w), add(v, u, w);    
            dfs1(1, 0);
            dfs2(1, 0);
            For(i, 1, n) ans += 1.0 * d[i] / du[i];
            printf("%.6lf
    ", ans / n);
            return ;
        }
    }T1;
    

    然后考虑环套树的情况

    基于 (Island)(Roads In The Kindom) 中的套路:

    把一棵基环树看成当前子树的根,然后分别处理掉,再对环上的点处理一下

    先算出来 (f_i) ,然后考虑环上的点的 (g_i) 值,可以往左侧走,也可以往右侧走

    可以暴力处理环上点的 (g_i) 值,然后再反向更新回去子树里面的 (g_i)

    (我实现能力可能是真的不行,写了一周,调了好久好久)

    struct task2{
        int cnt,rt, du[N], head[N];
        double t[N<<1],f[N], g[N], ans,res[N], d[N];
        struct node {
            int to, dis, nxt;
        } e[N << 1];
        inline void add(int u, int v, int w) {
            e[++cnt].to = v;
            e[cnt].nxt = head[u];
            e[cnt].dis = w;
            return head[u] = cnt, void();
        }
        pair<int,int> h[N<<1];
        int num,vis[N];
        inline bool find(int x,int fa,int pre)
        {
            if(vis[x]==1)
            {
                h[++num]=make_pair(x,e[pre].dis); vis[x]=3;
                return 1;
            } vis[x]=1;
            for(int i=head[x];i;i=e[i].nxt)
            {
                if(i==(pre^1)) continue;
                int t=e[i].to;
                if(find(t,x,i))
                {
                    if(vis[t]==3&&num!=1) return 0;
                    if(vis[x]!=3) h[++num]=make_pair(x,e[pre].dis);
                    return 1;   
                }
                if(num) return 0;
            }return 0;
        }
        inline void dfs1(int x,int fa)
        {
            for(reg int i=head[x];i;i=e[i].nxt)
            {
                int t=e[i].to; if(t==fa||vis[t]) continue;
                dfs1(t,x); d[x]+=f[t]+e[i].dis; du[x]++;
            }
            if(du[x]) f[x]=d[x]/du[x]; if(x!=rt) du[x]++;
            return ;
        }
        inline void dfs2(int x,int fa)
        {
            for(reg int i=head[x];i;i=e[i].nxt)
            {
                int t=e[i].to; if(t==fa||vis[t]) continue;
                double tmp=(res[x]*du[x]-e[i].dis-f[t])/max(1,du[x]-1);   
                res[t]=(f[t]*(du[t]-1)+tmp+e[i].dis)/du[t];
                dfs2(t,x); 
            } return ;
        }
        inline void main()
        {
            cnt=1; for(reg int i=1,u,v,w;i<=m;++i) u = read(), v = read(), w = read(), add(u, v, w), add(v, u, w);
            find(1,0,0); 
            memset(vis,0,sizeof(vis)); For(i,1,num) vis[h[i].first]=1;
            For(i,1,num) h[i+num]=h[i],rt=h[i].first,dfs1(h[i].first,0);
            
            For(i,1,num) 
            {
                int nd=h[i].second; 
                double np=1;
                For(j,i+1,num+i-1)
                {
                    if(j!=num+i-1) g[h[i].first]+=(nd+f[h[j].first]*du[h[j].first]/(du[h[j].first]+1))*np;
                    else g[h[i].first]+=(nd+f[h[j].first])*np;
                    nd=h[j].second; np/=(du[h[j].first]+1);
                }
                nd=h[i+num-1].second; np=1;
                Down(j,i+1,num+i-1)
                {
                    if(j!=i+1) g[h[i].first]+=(nd+f[h[j].first]*du[h[j].first]/(du[h[j].first]+1))*np;
                    else g[h[i].first]+=(nd+f[h[j].first])*np;
                    nd=h[j-1].second; np/=(du[h[j].first]+1);
                } 
            } 
            For(i,1,num) res[h[i].first]=(f[h[i].first]*du[h[i].first]+g[h[i].first])/(du[h[i].first]+2),du[h[i].first]+=2,dfs2(h[i].first,0);
            For(i,1,n) ans+=res[i];
            printf("%.6lf
    ",ans/n);
            return ;
        }
    }T2;
    

    Review

    (1.) 本题的最大收获就是 (g_i) 的转移一定要想清楚再写,改来改去浪费时间

    其实环的处理思路并不复杂,要不是说我把 (g_i) 想乱了,就是个水题……

    (2.) 更新自己找环的方法:

        inline bool find(int x,int fa,int pre)
        {
            if(vis[x]==1)
            {
                h[++num]=make_pair(x,e[pre].dis); vis[x]=3;
                return 1;
            } vis[x]=1;
            for(int i=head[x];i;i=e[i].nxt)
            {
                if(i==(pre^1)) continue;
                int t=e[i].to;
                if(find(t,x,i))
                {
                    if(vis[t]==3&&num!=1) return 0;
                    if(vis[x]!=3) h[++num]=make_pair(x,e[pre].dis);
                    return 1;   
                }
                if(num) return 0;
            }return 0;
        }
    

    改的地方主要是判断了个 (vis[t]==3) 否则在祖先那里会错……

    (3.) 正确学习期望线性性,这里主要是在暴力处理环那部分

    期望长度应该是每个边乘上概率加和,而不是路径长度乘上概率……

    (想出来环的那个可以 (O(len^2)) 做就真的不难……)

  • 相关阅读:
    js常见函数使用
    js数组与函数
    移动端响应式布局
    移动开发之rem布局
    移动flex布局
    移动流式布局
    [剑指offer] 矩阵覆盖
    [剑指offer] 变态跳台阶
    [剑指offer] 跳台阶
    [剑指offer] 斐波那契数列
  • 原文地址:https://www.cnblogs.com/yspm/p/13656532.html
Copyright © 2020-2023  润新知