【2018CPCP-Final G:】Pastoral Life in Stardew Valley

题目地址:https://codeforces.com/gym/102055/problem/G

题意:


给出一个n*m的网格,求有多少放置稻草人的方案,使得稻草人的周围都是庄稼,结果对10^9+7取模

 

解题思路:


对于第三个样例,可以缩小所选的网格,可以选择4*4的网格放稻草人,也可以选择3*4,4*3,3*3的网格放稻草人,总的方法数是这些不同网格大小对应的方法数之和。

由特殊推广到一般,对于一个n*m的网格,因为它的四周是庄稼,所以可以在(n-2)*(m-2)的网格大小内选择放置稻草人的位置(可以放多个稻草人),令x=n-2,y=m-2,即需要求在x*y的网格内有多少种放稻草人的方案.

我们知道x*y的网格内的矩形数目为\frac{x*(x+1)}{2}*\frac{y*(y+1)}{2}(详细推导请参考百度),令C(x)=\frac{x*(x+1)}{2}

  • x*y的网格可以分解为1个x行的网格,2个x-1行的网格,3个x-2行的网格,4个x-3行的网格,.....x个1行的网格
  • x*y的网格可以分解为1个y列的网格,2个y-1列的网格,3个y-2列的网格,4个y-3列的网格,.....y个1列的网格

上面一行分解的情况和下面一行分解的情况组合就是x*y的网格可以再分解的总的网格数目(注意是分解成了新的网格!!)

如果分解成了(x-1)*(y-3)的网格,那么这个新的网格内的矩形数目=2*4*C(x-1)*C(y-3),如果分解成了(x-2)*(y-2)的网格,那么新的网格内的矩形数目=3*3*C(x-2)*C(y-2)

所以可以知道对于一个x*y的网格,它能再分的所有网格内的矩形数目之和为:

(1*C(x)+2*C(x-1)+3*C(x-2)+4*C(x-3)+...+x*C(1))  *   (1*C(y)+2*C(y-1)+3*C(y-2)+4*C(y-3)+...+y*C(1))

乘号左侧和右侧两遍的式子形式都是一样的,对于1*C(x)+2*C(x-1)+3*C(x-2)+4*C(x-3)+...+x*C(1)我们可以用前缀和数组求:

令suma[i]=C(1)+C(2)+C(3)+...+C(i),sumb[i]=suma[1]+suma[2]+suma[3]+...+suma[i]

那么上面乘积的结果为sumb[x]*sumb[y]

综上所述:

对于输入的n和m,只要其中有一个数<3结果就是0;

否则结果就是sumb[n-2]*sumb[m-2]

 

ac代码:


#include
using namespace std;
int n,m;
const int mod = 1e9 + 7;
const int maxn = 1e5 + 10;
long long suma[maxn];
long long sumb[maxn];
long long c[maxn];
int main() {
	for(long long i = 1; i < maxn; i++) {
		c[i] = i * (i + 1) / 2;
		c[i] %= mod;
	}
	for(int i = 1; i < maxn; i++) {
		suma[i] = (suma[i - 1] + c[i]) % mod;
	}
	for(int i = 1; i < maxn; i++) {
		sumb[i] = (sumb[i - 1] + suma[i]) % mod;
	}
	int t;
	scanf("%d",&t);
	int ca = 0;
	while(t--) {
		scanf("%d%d",&n,&m);
		printf("Case %d: ",++ca);
		if(n < 3 || m < 3) {
			printf("0\n");
		}
		else
			printf("%lld\n",((sumb[n - 2] % mod) * (sumb[m - 2] % mod)) % mod);
	} 
	return 0;
}

 

你可能感兴趣的:(找规律题,数学题)