题目地址: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的网格内的矩形数目为(详细推导请参考百度),令C(x)=
- 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<bits/stdc++.h>
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;
}