• P2634 [国家集训队]聪聪可可


    题面

    Link

    题目描述

    聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃、两个人都想玩儿电脑(可是他们家只有一台电脑)……

    遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了这种低智商的游戏。

    他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画 (n) 个“点”,并用 (n−1) 条“边”把这 (n) 个“点”恰好连通

    (其实这就是一棵树)。并且每条“边”上都有一个数。接下来由聪聪和可可分别随即选一个点(当然他们选点时是看不到这棵树的),

    如果两个点之间所有边上数的和加起来恰好是 (3) 的倍数,则判聪聪赢,否则可可赢。

    聪聪非常爱思考问题,在每次游戏后都会仔细研究这棵树,希望知道对于这张图自己的获胜概率是多少。

    现请你帮忙求出这个值以验证聪聪的答案是否正确。

    输入格式

    输入的第 (1) 行包含 (1) 个正整数 (n)。后面 (n−1) 行,每行 (3) 个整数 (x),(y),(w),表示 (x) 号点和 (y) 号点之间有一条边,上面的数是 (w)

    输出格式

    以即约分数形式输出这个概率(即 a/b 的形式,其中 (a)(b) 必须互质。如果概率为 (1),输出 (1/1))。

    输入输出样例

    输入 #1

    5
    1 2 1
    1 3 2
    1 4 1
    2 5 3

    输出 #1

    13/25

    说明/提示

    【样例说明】

    131313 组点对分别是(1,1),(2,2),(2,3),(2,5),(3,2),(3,3),(3,4),(3,5),(4,3),(4,4),(5,2),(5,3),(5,5)。

    【数据规模】

    对于 100% 的数据,n≤2×10^4

    题解

    这几天刚学会点分治,所以拿道题练练手。

    好像这个题除了用点分治还可以用动态规划来解决。

    还是按套路,找出整棵树的重心,然后分治解决每个子树中的答案。

    我们主要看 (calc) 函数的实现。

    题目让求的是路径长度是三的倍数。

    我们可以记录一下每条路径 模以 3 之后的结果。

    (tong[i]) 表示余数为 (i) 的路径条数。

    首先一条 余数为2的路径和一条余数为1 路径的可以配成一条合法路径,也就是 (tong[2] imes tong[1] imes 2)

    乘 2 是因为 (x,y) 和 (y,x) 算两个点对

    两条余数为0的路径 也可以配成一条合法路径 即 (tong[0] imes tong[0])

    然后,我们这个点的答案就是 (tong[2] imes tong[1] imes 2 + tong[0] imes tong[0])

    calc 函数

    int calc(int x,int d)
    {
        tong[1] = tong[2] = tong[0] = 0;//每次计算都要清空一下
        dis[x] = d; cnt = 0;
        a[++cnt] = dis[x];
        for(int i = head[x]; i; i = e[i].net)
        {
            int to = e[i].to;
            if(vis[to]) continue;
            dis[to] = dis[x] + e[i].w;
            get_dis(to,x);
        }
        for(int i = 1; i <= cnt; i++)
        {
            tong[a[i]%3]++;//计算一下路径长度模以三的余数
        }
        return tong[1] * tong[2] * 2 + tong[0] * tong[0];
    }
    

    总体代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N = 2e4+10;
    int n,m,sum_siz,root,tot,cnt,ans,u,v,w;
    int head[N],max_siz[N],siz[N],dis[N],a[N],tong[10];
    bool vis[N];
    struct node
    {
    	int to,net,w;
    }e[N<<1];
    inline int read()
    {
        int s = 0,w = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
        while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
        return s * w; 
    }
    void add(int x,int y,int w)
    {
        e[++tot].w = w;
        e[tot].to = y;
        e[tot].net = head[x];
        head[x] = tot;
    }
    int gcd(int a,int b)
    {
        if(b == 0) return a;
        else return gcd(b,a%b);
    }
    void get_root(int x,int fa)//找重心
    {
        max_siz[x] = 0; siz[x] = 1;
        for(int i = head[x]; i; i = e[i].net)
        {
            int to = e[i].to;
            if(to == fa || vis[to]) continue;
            get_root(to,x);
            siz[x] += siz[to];
            max_siz[x] = max(max_siz[x],siz[to]);
        }
        max_siz[x] = max(max_siz[x],sum_siz-siz[x]);
        if(max_siz[x] < max_siz[root]) root = x;
    }
    void get_dis(int x,int fa)//找距离
    {
        a[++cnt] = dis[x];
        for(int i = head[x]; i; i = e[i].net)
        {
            int to = e[i].to;
            if(to == fa || vis[to]) continue;
            dis[to] = dis[x] + e[i].w;
            get_dis(to,x);
        }
    }
    int calc(int x,int d)//统计在 x 点的答案
    {
    	tong[1] = tong[2] = tong[0] = 0;
        dis[x] = d; cnt = 0;
        a[++cnt] = dis[x];
        for(int i = head[x]; i; i = e[i].net)
        {
            int to = e[i].to;
            if(vis[to]) continue;
            dis[to] = dis[x] + e[i].w;
            get_dis(to,x);
        }
        for(int i = 1; i <= cnt; i++)
        {
            tong[a[i]%3]++;
        }
        return tong[1] * tong[2] * 2 + tong[0] * tong[0];
    }
    void slove(int x)//点分治
    {
        ans += calc(x,0);
        vis[x] = 1;
        for(int i = head[x]; i; i = e[i].net)
        {
            int to = e[i].to;
            if(vis[to]) continue;
            ans -= calc(to,e[i].w);
            max_siz[0] = n; sum_siz = siz[to]; root = 0;
            get_root(to,0); slove(root);
        }
    }
    int main()
    {
        n = read(); 
        for(int i = 1; i <= n-1; i++)
        {
            u = read(); v = read(); w = read();
            add(u,v,w); add(v,u,w);
        }
        max_siz[0] = sum_siz = n; root = 0;
        get_root(1,0); slove(root);//找整棵树的重心
        int d = gcd(ans,n*n);//约分
        printf("%d/%d",ans/d,(n*n)/d);
        return 0;
    }
    
    
  • 相关阅读:
    jmeter根据负载量计算并发用户数实例
    学生指导——德育
    命令行模式(非GUI模式)下执行jmeter,生成HTML性能测试报告,自定义线程数;
    jmeter 测试某系统5分钟内能完成5000笔查询业务,且90%的响应时间不超过3s,并求出需要设置的线程数
    jmeter 测试某网页最大并发用户数;
    ZOJ 3213
    POJ 2411 插头DP
    滑雪(ski)
    Puzzles
    Lorenzo Von Matterhorn
  • 原文地址:https://www.cnblogs.com/genshy/p/13601415.html
Copyright © 2020-2023  润新知