方格计数
传送门
题意:
在左下角是 \((0, 0)\),右上角是 \((W, H)\) 的网格上,有 \((W + 1) × (H + 1)\) 个格点。 现在要在格点上找 N个不同的点,使得这些点在一条直线上。并且在这条直线上,
相邻点之间的距离不小于 D。求方案数模 \(10^9 + 7\)。
做法:
枚举矩形(右上角端点 \((x,y)\) ),以其两条对角线(如果长或宽为 0 则只有一条)作为所需直线,强制选其两个端点,中间有 \(gcd(x,y)-1\) 个点。
需满足两点距离间隔不小于 D,计算出最少要间隔几个点。
在 \(n\) 个点中选取 \(m\) 个点并且选的两点之间至少要有 \(k\) 个点的方案数:\(C_{n-(m-1)*k} ^m\)
套入计算即可,最后乘上矩形可平移范围 \((w-x+1)*(h-y+1)\)。
code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=505;
ll c[N][N];
ll mod=1e9+7;
ll n,w,d,h;
int gcd(int x,int y)
{
if(!y) return x;
return gcd(y,x%y);
}
double calc(int x,int y)
{
return sqrt((x*x*1.0+y*y));
}
ll solve(ll x,ll y)
{
if(!x&&!y) return 0;
ll g=gcd(x,y);
int k=(int)(ceil(d/calc(x/g,y/g)));
if(k*(n-1)>g ) return 0;
ll ans=c[g-1-2*(k-1)-(k-1)*(n-3)][n-2];
if(x&&y) ans<<=1,ans%=mod;
return ans*(w-x+1)%mod*(h-y+1)%mod;
}
signed main()
{
freopen("B.in","r",stdin);
freopen("B.out","w",stdout);
for(int i=0;i<=500;i++)
{
c[i][0]=c[i][i]=1;
for(int j=1;j<i;j++)
c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
int T;
cin>>T;
while(T--)
{
scanf("%lld%lld%lld%lld",&n,&w,&h,&d);
if(n==1){
printf("%lld\n",(w+1)*(h+1));
continue;
}
ll ans=0;
for(int i=0;i<=w;i++)
for(int j=0;j<=h;j++)
ans=(ans+solve(i,j))%mod;
printf("%lld\n",ans);
}
}