Codeforcces 478D Red-Green Towers【dp】好题!

D. Red-Green Towers
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

There are r red and g green blocks for construction of the red-green tower. Red-green tower can be built following next rules:

  • Red-green tower is consisting of some number of levels;

  • Let the red-green tower consist of n levels, then the first level of this tower should consist of n blocks, second level — of n - 1 blocks, the third one — of n - 2 blocks, and so on — the last level of such tower should consist of the one block. In other words, each successive level should contain one block less than the previous one;

  • Each level of the red-green tower should contain blocks of the same color.

Let h be the maximum possible number of levels of red-green tower, that can be built out of r red and g green blocks meeting the rules above. The task is to determine how many different red-green towers having h levels can be built out of the available blocks.

Two red-green towers are considered different if there exists some level, that consists of red blocks in the one tower and consists of green blocks in the other tower.

You are to write a program that will find the number of different red-green towers of height h modulo 109 + 7.

Input

The only line of input contains two integers r and g, separated by a single space — the number of available red and green blocks respectively (0 ≤ r, g ≤ 2·105, r + g ≥ 1).

Output

Output the only integer — the number of different possible red-green towers of height h modulo 109 + 7.

Examples
Input
4 6
Output
2
Input
9 7
Output
6
Input
1 1
Output
2
Note

The image in the problem statement shows all possible red-green towers for the first sample.


题目大意:

一共有r个红色正方体,有g个绿色正方体,按照题目中给出的图示方式堆起来(每一行必须相同颜色,第一行一个正方体,第二行两个,第三行三个..),问一共有多少种方案。


思路:


1、首先我们分析题干中的因素:

①我们能够通过r和g的总数,来判断最高可能有多高。

②我们需要讨论每种正方体用了多少个。

③我们还需要讨论当前行到底用了哪种颜色。


2、那么我们可以设定dp【i】【j】【k(0红/1绿)】表示当前堆砌到第i行,已经使用颜色k的正方体的个数为j的方案数。

那么我们不难推出其状态转移方程:

dp【i】【j】【0】+=dp【i-1】【j-i】【0】;

dp【i】【j】【1】+=dp【i-1】【j-1】【1】;

表示这一行和上一行的颜色相同的状态转移方式,如果当前行使用了当前颜色的正方体个数为j,那么上一行一定使用当前这种颜色的正方体的个数为j-i。

②tot=(i+1)*i/2;

dp【i】【j】【0】+=dp【i-1】【tot-j】【1】;

dp【i】【j】【1】+=dp【i-1】【tot-j】【0】;

表示这一行和上一行的颜色不同的状态转移方式,我们知道,到第i行,一共堆砌的正方体个数为(i+1)*i/2;那么当前行使用了j个颜色为0的正方体,那么上一行一定使用另外一种颜色的正方体的个数为tot-j。

③注意各个变量的可行范围。

④那么此时我们最终答案:

output+=dp【n】【i】【0】(n<=i<=r)

output+=dp【n】【i】【1】(n<=i<=g)


3、然后分析这样一点,对应最高层数通过计算可以估算出,最高层数可能接近于1000层,对应数组开了那么大,是一定吃不消的。

那么我们再继续考虑优化空间。

我们不难观察出,当前行的状态只与上一行的状态相关联,而且我们最终答案只与最终层直接相关,那么我们可以将数组开到dp【2】【r+g】【2】即可。

那么我们在dp完第二行的时候,第一行的内容就可以不要了,那么我们此时将第二行的信息存入dp【1】【】【】中,然后dp第三行存入dp【2】【】【】,同理,dp完第三行之后,再将第三行的信息存入dp【1】【】【】中,依次类推。

最终统计答案的时候,在dp【1】【】【】中累加即可。


4、注意一些细节、


Ac代码:

#include<stdio.h>
#include<string.h>
using namespace std;
#define mod 1000000007
int dp[3][400050][2];
int main()
{
    int r,g;
    while(~scanf("%d%d",&r,&g))
    {
        int n;
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=1000;i++)
        {
            if(i*(i+1)/2>=r+g)
            {
                n=i;
                if(i*(i+1)/2>r+g)n--;
                break;
            }
        }
        if(r>0)
        dp[1][1][0]=1;
        if(g>0)
        dp[1][1][1]=1;
        for(int i=2;i<=n;i++)
        {
            int tot=(1+i)*i/2;
            for(int j=1;j<=tot;j++)
            {
                if(j-i>=0)
                {
                    if(j<=r)
                    dp[2][j][0]=(dp[2][j][0]+dp[1][j-i][0])%mod;
                    if(j<=g)
                    dp[2][j][1]=(dp[2][j][1]+dp[1][j-i][1])%mod;
                    if(j<=tot-i+1)
                    {
                        if(j<=r)
                        dp[2][j][0]=(dp[2][j][0]+dp[1][tot-j][1])%mod;
                        if(j<=g)
                        dp[2][j][1]=(dp[2][j][1]+dp[1][tot-j][0])%mod;
                    }
                }
            }
            for(int j=1;j<=tot;j++)
            {
                dp[1][j][0]=dp[2][j][0];
                dp[2][j][0]=0;
                dp[1][j][1]=dp[2][j][1];
                dp[2][j][1]=0;
            }
        }
        int output=0;
        for(int i=n;i<=r;i++)
        {
            output=(output+dp[1][i][0])%mod;
        }
        for(int i=n;i<=g;i++)
        {
            output=(output+dp[1][i][1])%mod;
        }
        printf("%d\n",output);
    }
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值