B - Chessboard
分析:
-
阅读理解题,组合数学,神仙思维
" the shortest distance between any two blocks would remain unchanged after they were painted"
主要是这句话,不能让任意两个访问过的格子的最短距离发生改变,想一下可以发现以下几条规则:
-
首先,从始至终都是在同一个连通块,然后不断向外扩展
-
到最后,画满的情况下,任意两点的最短路都是曼哈顿距离
-
最后,结束的地方一定是四角之一
为了满足上述几个规则,在画的过程中,在同一列(同一行)出现未被访问的格子夹在已访问的格子中间的情况是绝对不允许的
为了避免这种情况的发生,就要按照矩形的方式去扩展(一行一行,一列一列)
-
-
刚开始,选一个点,即 1 × 1 1\times 1 1×1的矩形,那么接下来,就要变成 2 × 1 2\times1 2×1 or 1 × 2 1\times2 1×2的矩形
若变成 r × c r\times c r×c,接下来扩展行( ( r + 1 ) × c (r+1)\times c (r+1)×c) 扩展列( r × ( c + 1 ) r\times(c+1) r×(c+1))
到最终变成 n × m n\times m n×m,总共是扩展 ( n + m − 2 ) (n+m-2) (n+m−2)次,行扩展 ( n − 1 ) (n-1) (n−1)次,贡献为 ( n − 1 n + m − 2 ) (^{n+m-2}_{n-1}) (n−1n+m−2)
再根据终点不同,逆推回去,扩展方式都是唯一的,方案数再乘4即可
-
注意:特判n=1 or m=1 的情况
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e6+5, mo=1e9+7;
int ksm(int a,int b,int p=mo)
{
int ans=1;
while(b)
{
if(b&1) ans=ans*a%p;
b>>=1; a=a*a%p;
}
return ans;
}
int fac[N], inv[N];
void init(int n)
{
fac[0]=inv[0]=1;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mo;
inv[n]=ksm(fac[n],mo-2);
for(int i=n-1;i>=1;i--) inv[i]=inv[i+1]*(i+1)%mo;
}
int C(int n,int m) // 组合数
{
return fac[n]*inv[m]%mo*inv[n-m]%mo;
}
int f[N];
void solve()
{
int n,m;
scanf("%lld%lld",&n,&m);
if(n==1 && m==1) cout<<"1\n";
else if(n==1 || m==1) cout<<"2\n";
else cout<<C(n+m-2,n-1)*4%mo<<"\n";
}
signed main()
{
init(N-2);
int t=1;
cin>>t;
while(t--) solve();
}