1. 程式人生 > >【打CF,學演算法——五星級】CodeForces 478D (dp計數)

【打CF,學演算法——五星級】CodeForces 478D (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.

題意:

     給定紅綠兩種方塊,要求以1,2,3..n的方式,一層一層壘上去,且任意一層只能是單色,問在該種方式下壘到最高高度的方式有多少種?

解題:

     因為是1,2,3..所以只要剛剛好方塊夠到某一層的總數量的話,是一定可以構造出可行方案的,因此可以直接根據兩種方塊數量總和,先求出最高高度。根據範圍,最高高度為1000。可以比較輕鬆地想到用dp[i][j]來表示,其中i為第幾層,j為到該層為止用了多少紅色方塊,dp的值的含義是在第i層用了j塊紅色方塊的方案數。此方法看似可行,實則不然,1000*(2*10^5)遠超出題目給的空間限定範圍,且複雜度也夠嗆。因為,我們最後要取的僅僅是最後一層的結果,前面的計算過程都是不需要的,因此,我們可以採用滾動陣列的方法,將第一維壓縮至2,分別表示上一層和下一層。在計算過程中用佇列或其他資料結構儲存要更新的點。最後取最後一層的所有結果和即可。

程式碼:

#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#include <vector>
#define LL long long
#define maxn 200010
#define sq(a)  1LL*(a)*(a)
#define mod 1000000007
using namespace std;
bool vis[maxn];
vector <int> v[2];
int amount[1000];
int dp[2][maxn];
void init()
{
	for(int i=1;i<1000;i++)
		amount[i]=(1+i)*i/2;
}
int main()
{
	int r,g,total,h,a,b,sz,tmp,ans=0,tmp2;
	init();
	scanf("%d%d",&r,&g);
    total=r+g;
    h=(-1+sqrt(1+8.0*total))/2;
	if(g>0)
	{
		dp[0][0]=1;
		v[0].push_back(0);
	}
	if(r>0)
	{
		dp[0][1]=1;
		v[0].push_back(1);
	}

	a=0;
	b=1;
	for(int i=2;i<=h;i++)
	{
	   sz=v[a].size();
	   for(int i=0;i<sz;i++)
		   vis[v[a][i]]=0;
	   for(int j=0;j<sz;j++)
	   {

		   tmp=amount[i]-v[a][j];
		   if(tmp<=g)
		   {
			   if(!vis[v[a][j]])
			   {
				   v[b].push_back(v[a][j]);
				   dp[b][v[a][j]]=0;
				   vis[v[a][j]]=1;
			   }
			   dp[b][v[a][j]]=(dp[b][v[a][j]]+dp[a][v[a][j]])%mod;
		   }
		   tmp=v[a][j]+amount[i]-amount[i-1];
           if(tmp<=r)
		   {
			   if(!vis[tmp])
			   {
				   v[b].push_back(tmp);
				   vis[tmp]=1;
				   dp[b][tmp]=0;
			   }
			   dp[b][tmp]=(dp[b][tmp]+dp[a][v[a][j]])%mod;
		   }
		 }
	   v[a].clear();
	   swap(a,b);
	}
	sz=v[a].size();
	for(int i=0;i<sz;i++)
		ans=(ans+dp[(h+1)%2][v[a][i]])%mod;
	printf("%d\n",ans);
	return 0;
}