B - Chessboard

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+m2)次,行扩展 ( n − 1 ) (n-1) (n1)次,贡献为 ( n − 1 n + m − 2 ) (^{n+m-2}_{n-1}) (n1n+m2)

    再根据终点不同,逆推回去,扩展方式都是唯一的,方案数再乘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();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yezzz.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值