• P2634 [国家集训队]聪聪可可 [点分治(待填坑) /树形dp]


    聪聪可可

    聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃、两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了这种低智商的游戏。

    他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画n个“点”,并用n-1条“边”把这n个“点”恰好连通(其实这就是一棵树)。并且每条“边”上都有一个数。接下来由聪聪和可可分别随即选一个点(当然他们选点时是看不到这棵树的),如果两个点之间所有边上数的和加起来恰好是3的倍数,则判聪聪赢,否则可可赢。

    聪聪非常爱思考问题,在每次游戏后都会仔细研究这棵树,希望知道对于这张图自己的获胜概率是多少。现请你帮忙求出这个值以验证聪聪的答案是否正确。

    n<=20000n<=20000.


    color{blue}{最初想法}

    点分治? 不会, 打暴力跳过…


    color{red}{正解部分}

    1:方法 1: 树形dp

    F[i,j]F[i, j] 表示一个端点为 ii 点, 含有多少边权和 %3=j\%3=j的路径,
    因为自己到自己为一条路径, 所以初值 F[i,0]=1F[i,0]=1, 利用子树进行转移, 具体地说:

    从头到尾扫所有子树, 设扫到中间时, 已有的路径数为 F[i,j]F[i, j], 当前子树的路径数为 F[to,j]F[to, j],

    • 更新答案: Ans +=2F[i,j]F[to,3j]Ans +=2* F[i, j]*F[to,3-j]
    • 更新dpdp数组: F[i,j] +=F[to,((jedge[p].w)%3+3)%3)]F[i, j] += F[to, ((j-edge[p].w)\%3+3)\%3)] .

    两个端点都为自己的路径 在上述累加方式中没有涉及到, 所以最后答案要加上 NN .


    2:方法 2: 点分治

    .待填坑.


    color{red}{实现部分}

    • 注意最后答案要加上 NN .
    • 注意统计答案时要乘 22 .
    • 在处理模意义下的负数时不能够直接取绝对值 .
    #include<bits/stdc++.h>
    #define reg register
    
    const int maxn = 20004;
    
    int N;
    int Ans;
    int num0;
    int head[maxn];
    int F[maxn][3];
    
    struct Edge{ int nxt, to, w; } edge[maxn<<1];
    
    void Add(int from, int to, int w){
            edge[++ num0] = (Edge){ head[from], to, w };
            head[from] = num0;
    }
    
    int Mod(int x){ return (x%3+3) % 3; }
    
    int Gcd(int a, int b){ return !b?a:Gcd(b, a%b); }
    
    void DFS(int k, int fa){
            F[k][0] = 1;
            for(reg int i = head[k]; i; i = edge[i].nxt){
                    int to = edge[i].to;
                    if(to == fa) continue ;
                    DFS(to, k);
                    for(reg int j = 0; j < 3; j ++) Ans += 2*F[k][j]*F[to][Mod(-j-edge[i].w)];
                    for(reg int j = 0; j < 3; j ++) F[k][j] += F[to][Mod(j-edge[i].w)];
            }
    }
    
    int main(){
            scanf("%d", &N);
            for(reg int i = 1; i < N; i ++){
                    int x, y, w;
                    scanf("%d%d%d", &x, &y, &w); w %= 3;
                    Add(x, y, w), Add(y, x, w);
            }
            DFS(1, 0);
            Ans += N; //!
            int tot = N*N;
            int gcd = Gcd(tot, Ans);
            Ans /= gcd, tot /= gcd;
            printf("%d/%d
    ", Ans, tot);
            return 0;
    }
    
    
  • 相关阅读:
    dremio 连接gitbase
    pf4j 试用
    pf4j 默认系统扩展以及机制说明
    sbp 基于 pf4j开发的spring boot 扩展框架
    dremio的分布式存储简单说明
    waitforit 基于shell 的tcp 状态检测工具
    hive metadata 容器镜像
    pf4j zip 插件的处理
    lakefs metadata 集成
    lakefs 集成presto/trinodb
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822507.html
Copyright © 2020-2023  润新知