正題
題目連結:https://ac.nowcoder.com/acm/contest/20107/B
題目大意
給出一個\(w\times h\)的網格圖,然後要求在上面選出\(n\)個格點,使得它們在一條直線上且兩兩之間距離不小于\(d\)。
\(1\leq T\leq 20,1\leq w,h,d\leq 500,1\leq n\leq 50\)
解題思路
先隻考慮橫豎和斜向右下的直線
顯然是枚舉直線更加迅速,可以枚舉一個斜率\(\frac{a}{b}\),然後為了防止算重我們考慮起點也就是我們選擇的最第一個點,對于起點在\((0,0)\sim (w-ka,h-kb)\)的矩形内的點,右下角至少還有\(k\)個點可以選擇,我們可以枚舉這個\(k\),然後暴力統計。這樣一次複雜度是\(O(min\{\frac{w}{a},\frac{h}{b}\})\),類似于調和級數不是很大。
之後考慮統計選擇的方案,設\(f_{i,j,k}\)表示選擇的兩個之間要至少相隔\(i\),有\(j\)個可以選,選擇\(k\)個的方案,這個可以直接\(dp\)。
就可以過了,時間複雜度:\(O(whn+Twh\log {wh})\)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;
const ll N=510,P=1e9+7;
ll T,n,w,h,D,ans,d[N][N];
int f[N][N][N];
signed main()
{
scanf("%lld",&T);
for(ll i=1;i<=500;i++)d[i][0]=d[0][i]=i;
for(ll i=1;i<=500;i++)
for(ll j=1;j<=i;j++)d[i][j]=d[j][i]=d[j][i%j];
for(ll i=1;i<=500;i++){
f[i][0][0]=1;
for(ll j=1;j<=500;j++){
for(ll k=0;k<=500;k++)f[i][j][k]=f[i][j-1][k];
if(j>=i)
for(ll k=1;k<=500;k++)
(f[i][j][k]+=f[i][j-i][k-1])%=P;
}
}
while(T--){
scanf("%lld%lld%lld%lld",&n,&w,&h,&D);
if(n==1){printf("%lld\n",(w+1)*(h+1)%P);continue;}
ans=0;
for(ll i=0;i<=w;i++)
for(ll j=0;j<=h;j++){
if(d[i][j]!=1)continue;
ll k,last=0,ub=1;
if(i!=0&&j!=0)ub=2;
ll dis=ceil(D/sqrt(i*i+j*j));
for(k=1;k;k++){
if(k*i>w||k*j>h)break;
ll L=w-k*i+1;ll R=h-k*j+1;
(ans+=L*R%P*(1ll*(f[dis][k][n-1]-f[dis][k-1][n-1]))*ub%P)%=P;
}
}
printf("%lld\n",(ans+P)%P);
}
return 0;
}