• BZOJ 2152 聪聪可可(树形DP)


    聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃、两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了这种低智商的游戏。他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画n个“点”,并用n-1条“边”把这n个“点”恰好连通(其实这就是一棵树)。并且每条“边”上都有一个数。接下来由聪聪和可可分别随即选一个点(当然他们选点时是看不到这棵树的),如果两个点之间所有边上数的和加起来恰好是3的倍数,则判聪聪赢,否则可可赢。聪聪非常爱思考问题,在每次游戏后都会仔细研究这棵树,希望知道对于这张图自己的获胜概率是多少。现请你帮忙求出这个值以验证聪聪的答案是否正确。


    Input

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

    Output

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

    Sample Input5 1 2 1 1 3 2 1 4 1 2 5 3

    Sample Output13/25 【样例说明】 13组点对分别是(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<=20000。


    题解:题目让你求有多少组点对,满足(x,y)在x到y之间的便的权值和是3的倍数。

    树形DP,dp[r][x]:表示以r为根的子树中到r的距离%3==x的数量;则很容易想到:

    for(int i=0;i<3;++i) ans+=1ll*dp[v][i]*dp[x][((-i-w)%3+3)%3]*2;
    for(int i=0;i<3;++i) dp[x][(i+w)%3]+=dp[v][i];

    的转移方式:


    参考代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define PI aocs(-1.0)
    #define pii pair<int,int>
    #define mod 1000000007
    typedef long long ll;
    const int INF=0x3f3f3f3f;
    const int maxn=20010;
    int n,u,v,w;
    int head[maxn<<1],cnt;
    ll ans,ans1,dp[maxn][3];
    struct Edge{
        int v,w,nxt;
    } edge[maxn<<1];
    
    void addedge(int u,int v,int w)
    {
        edge[cnt].v=v;
        edge[cnt].w=w;
        edge[cnt].nxt=head[u];
        head[u]=cnt++;
    }
    
    void dfs(int x,int fa)
    {
        dp[x][0]=1;
        for(int e=head[x];~e;e=edge[e].nxt)
        {
            int v=edge[e].v,w=edge[e].w;
            if(v==fa) continue;
            dfs(v,x);
            for(int i=0;i<3;++i) ans+=1ll*dp[v][i]*dp[x][((-i-w)%3+3)%3]*2;
            for(int i=0;i<3;++i) dp[x][(i+w)%3]+=dp[v][i];
        }
    }
    
    int main()
    {    
        scanf("%d",&n);
        cnt=0; ans=0; ans1=n*n*1ll;
        memset(head,-1,sizeof(head));
        memset(dp,0,sizeof(dp));
        for(int i=1;i<n;++i)
        {
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w); addedge(v,u,w);
        }
        dfs(1,0); ans+=1ll*n;
        ll g=__gcd(ans,ans1);
        ans/=g;ans1/=g;
        printf("%lld/%lld
    ",ans,ans1);
    
        return 0;
    }
    View Code
  • 相关阅读:
    《算法导论》第十章----基本数据结构
    《算法导论》第九章----中位数和顺序统计学
    《算法导论》第八章----线性时间排序(决策树+计数排序+基数排序)
    C++实现快速排序
    C++实现斐波那契第N项非递归与递归实现的时间比较
    C++实现用两个栈实现队列
    C++实现从尾到头打印链表(不改变链表结构)
    C++实现二叉树(建树,前序,中序,后序)递归和非递归实现
    Spark 大数据文本统计
    逻辑回归--参数解释+数据特征不独热编码+训练数据分布可视话
  • 原文地址:https://www.cnblogs.com/csushl/p/10907912.html
Copyright © 2020-2023  润新知