zoukankan      html  css  js  c++  java
  • 省选模拟赛 LYK loves rabbits(rabbits)

    题目描述

    LYK喜欢兔子,它在家中养了3只兔子。

    有一天,兔子不堪寂寞玩起了游戏,3只兔子排成一排,分别站在a,b,c3个位置。

    游戏的规则是这样的,重复以下步骤k次:选择两个不同的兔子AB,假如它们位于XYA可以从X跳到Y+Y-X处,但是跳跃时是不允许一下子跳过两只兔子的,也就是说第三只兔子不在[min{X,Y+Y-X},max{X,Y+Y-X}]处。

    现在3只小兔子的位置分别到了x,y,z(3只兔子长得一样,即三只兔子跳完之后的顺序可以变化)处,但是它们忘记一开始是怎么跳的了,想让你帮它们还原跳法。但这个问题非常easy,于是LYK要求你输出方案总数。

    保证答案有解。

    由于答案巨大,你只需输出答案对1e9+7取模后的结果就可以了。

    输入格式(rabbits.in)

    第一行3个数a,b,c

    第二行3个数x,y,z

    第三行一个数k

    数据保证3只兔子的起始位置a,b,c严格递增且3只兔子最终的位置x,y,z严格递增。

    输出格式(rabbits.out)

    一行表示方案总数。

    输入样例1

    0 2 5

    0 2 5

    2

    输出样例1

    3

    输入样例2

    0 2 4

    0 2 4

    2

    输出样例2

    2

    样例解释

    对于样例1:共有3种跳法,第一次跳完后的位置分别是{0,-2,5},{4,2,5},{0,8,5}

    数据范围

    对于100%的数据k<=100,|a|,|b|,|c||x|,|y|,|z|<=10^18

    分析:非常神奇的一道题.

       将3只兔子所在的位置看成一个状态. 那么起点状态S(a,b,c),终点状态T(x,y,z).  

       如果中间的兔子往两边跳,那么它能转移到2个新的状态. 如果外面的兔子想往里面跳,那么最多只能有一个兔子往里跳. 这些状态其实构成了一棵二叉树:如果中间的兔子往两边跳,那么就转移到2个儿子节点中,如果两边的兔子往里面跳,就转移到父亲节点中.

       那么现在的问题就变成了:在一棵二叉树中,求节点S到节点T恰好经过k条边的方案数.

       两个点的路径只与它们到LCA之间的路径有关. 令f(i,j,k)表示S与LCA(S,T)之间的距离为i,T与LCA(S,T)之间的距离为j,还能走k条边的方案数. 固定T,只考虑S的移动,分以下几种情况讨论:

       1.如果S已经是LCA了. 如果T也是LCA了. S可以往上走也可以往两个儿子节点走. f(i,j,k) += f(i + 1,j,k - 1) * 2 + f(i,j - 1,k - 1). 乘2是因为有2个儿子节点可以走.  如果T不是LCA. 那么可以走到父亲节点,走到不是T的那个子树和走到T所在的那个子树上. f(i,j,k) += f(i,j + 1,k - 1) + f(i,j - 1,k - 1) + f(i + 1,j,k - 1).

       2.如果S不是LCA. S既可以走到2个儿子节点,也可以走到父亲节点. 转移和上面的类似.

       知道了如何求方案数,该怎么建树呢?

       首先需要明确的是:我们不需要建出整棵树. 因为树的结构就是一棵完全二叉树,每一棵子树的形态结构都是一样的. 根据dp需要的信息,我们只需要求出S和T距离LCA的深度即可.

       先要求出LCA. LCA处的两个状态的兔子的位置全都相同. 让S状态的外面的兔子不断往里跳,T状态的也如此. 最后肯定会有两个状态的兔子的位置完全相等,最早出现的这个状态就是LCA.  为什么要往里跳呢?因为只有这样才能枚举出优先的状态. 否则二叉树就会无限向下扩展.

       小结一下. 做这道题首先要想到把3个兔子的位置看作一个状态. 状态有3种不同的转移方式. 其中有2种的本质是相同的. 联想到二叉树. 求状态A到B的方案数实际上就是求路径数. 树上路径只和这两个点以及它们的LCA有关,由此可以设计出状态. 最后求LCA运用中间相遇法. 思路还是挺巧妙的.

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long ll;
    const int maxn = 1010,mod = 1e9+7;
    ll a[maxn],b[maxn],c[maxn],x[maxn],y[maxn],z[maxn],k,k1,k2,pos1,pos2,f[110][110][110];
    bool flag = false;
    
    ll dfs(ll X,ll Y,ll K)
    {
        if (!X && !Y && !K)
            return 1;
        if (X + Y > K)
            return 0;
        if (!X && Y > k2)
            return 0;
        if (f[X][Y][K] != -1)
            return f[X][Y][K];
        if (!X)
        {
            if (!Y)
                f[X][Y][K] = (dfs(X,Y + 1,K - 1) + dfs(X + 1,Y,K - 1) * 2 % mod) % mod;
            else
                f[X][Y][K] = ((dfs(X,Y + 1,K - 1) + dfs(X,Y - 1,K - 1)) % mod + dfs(X + 1,Y,K - 1)) % mod;
        }
        else
            f[X][Y][K] = (dfs(X + 1,Y,K - 1) * 2 % mod + dfs(X - 1,Y,K - 1)) % mod;
        return f[X][Y][K];
    }
    
    int main()
    {
        memset(f,-1,sizeof(f));
        scanf("%lld%lld%lld%lld%lld%lld%lld",&a[0],&b[0],&c[0],&x[0],&y[0],&z[0],&k);
        while (k1 <= k)
        {
            if (b[k1] - a[k1] == c[k1] - b[k1])
                break;
            if (b[k1] - a[k1] < c[k1] - b[k1])
            {
                k1++;
                a[k1] = b[k1 - 1];
                b[k1] = 2 * b[k1 - 1] - a[k1 - 1];
                c[k1] = c[k1 - 1];
            }
            else
            {
                k1++;
                a[k1] = a[k1 - 1];
                c[k1] = b[k1 - 1];
                b[k1] = 2 * b[k1 - 1] - c[k1 - 1];
            }
        }
        while (k2 <= k)
        {
            if (y[k2] - x[k2] == z[k2] - y[k2])
                break;
            if (y[k2] - x[k2] < z[k2] - y[k2])
            {
                k2++;
                x[k2] = y[k2 - 1];
                y[k2] = 2 * y[k2 - 1] - x[k2 - 1];
                z[k2] = z[k2 - 1];
            }
            else
            {
                k2++;
                x[k2] = x[k2 - 1];
                z[k2] = y[k2 - 1];
                y[k2] = 2 * y[k2 - 1] - z[k2 - 1];
            }
        }
        for (pos1 = 0; pos1 <= k1 && !flag; pos1++)
            for (pos2 = 0; pos2 <= k2 && !flag; pos2++)
                if (a[pos1] == x[pos2] && b[pos1] == y[pos2] && c[pos1] == z[pos2])
                    flag = true;
        pos1--;
        pos2--;
        printf("%lld
    ",dfs(pos1,pos2,k) % mod);
    
        return 0;
    }
  • 相关阅读:
    luogu P1833 樱花 看成混合背包
    luogu P1077 摆花 基础记数dp
    luogu P1095 守望者的逃离 经典dp
    Even Subset Sum Problem CodeForces
    Maximum White Subtree CodeForces
    Sleeping Schedule CodeForces
    Bombs CodeForces
    病毒侵袭持续中 HDU
    病毒侵袭 HDU
    Educational Codeforces Round 35 (Rated for Div. 2)
  • 原文地址:https://www.cnblogs.com/zbtrs/p/8647862.html
Copyright © 2011-2022 走看看