#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);} #define MS(x,y) memset(x,y,sizeof(x)) #define MC(x,y) memcpy(x,y,sizeof(x)) #define MP(x,y) make_pair(x,y) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;} template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;} const int N=900,M=2e5+2,Z=1e9+7,ms63=1061109567; int n,m; int f[2][M]; //f[][i]表示还剩下i个红色方块的方案数 void add(int &x,int y) { x+=y; if(x>=Z)x-=Z; } int main() { while(~scanf("%d%d",&n,&m)) { //(top+1)top<=2(n+m) //top+0.<=sqrt(2(n+m)) int top=sqrt(n+m<<1); while((1+top)*top/2>(n+m))--top; int sum=0; int now=0; int nxt=1; MS(f[now],0);f[0][n]=1; for(int i=1;i<=top;++i) { MS(f[nxt],0); for(int red=0;red<=n;++red)//枚举之前剩下的红色的个数 { int green=m+n-sum-red;//其实可以直接在for循环中使得red<+m+n-sum,或者加很多剪枝的限制条件来加速 if(green<0)break; if(red>=i)add(f[nxt][red-i],f[now][red]); if(green>=i)add(f[nxt][red],f[now][red]); } sum+=i; now^=1; nxt^=1; } int ans=0; for(int red=0;red<=n;++red)add(ans,f[now][red]); printf("%d\n",ans); } return 0; } /* 【trick&&吐槽】 观察真实的数据范围,不要被很大的数字吓到了哦! 【题意】 有红绿两色的方块,数量分别为r个和g个。有0<=r,g<=2e5,r+g>=1 我们想用这红绿两色的方块(不一定全部,可以是一部分),构成一个高度尽可能高的积木块。 这个积木块需要满足—— 1,假设高度为h,那么第一层有1个,第二层有2个,……,第h层有h个。 2,每层方块的颜色,不是全部为红色,就是全部为绿色。 让你输出,有多少种方案,可以构成高度最高的积木块。 【类型】 DP 滚动数组 【分析】 一定要一定要好好地分析数据规模! 这题虽然r和g的数量都可达2e5,然而,事实上可以最高达成的楼层高度,不过只有 设最高楼层的高度为top,那么,我们有——(top+1)top/2 <= n+m 显然,(top+1)top<=2(n+m),即top+0. <= 2(n+m) 显然,我们求得sqrt(2(n+m))是必然>=top的。 于是,求得一个[sqrt(2(n+m))]最为top的最大情况。 然而,这个top可能超过实际最大楼层,于是我们要有——while((1+top)*top/2>(n+m))--top; 为什么,现在的top就一定是最大楼层呢? 因为,这个楼层的搭建,总数是为1~top的和, 于是,这个划分,这个数字的拆分,可以通过极其灵活的方式搭配,于是就可以(这可是男人的直觉,哼!) 而且数量的切分上,我们是可以切出最大一层的。 于是,我们有了最大层数,而且其一定不超过899。 所以,接下来直接用f[i][j]表示现在已经搭了第1~i层,并且还剩下j块红色木块的方案数。 显然有—— 1,初始条件为f[0][n]=1; 2,DP方程为 枚举楼层,枚举之前所有楼铺完还剩下的红色木块数,然后—— 我们可以通过一共使用的木块数,求得现在还剩下的绿色木块数。 如果现有红色木块数或者绿色木块数比铺这层楼需要的木块数多,我们就可以进行更新—— 因为空间消耗可达900*2e5,会爆炸。 所以我们用滚动数组就可以AC啦 【时间复杂度&&优化】 O(sqrt(n+m)n) 【数据】 input 200000 200000 output 206874596 */