题意:
给n*m的点阵,问有多少条线通过至少两个点,且不竖直,不水平。
解析:
其实求的就是整个点阵有多少条斜线啦。
cnt[i][j]代表 点(i,j)到左上角,可以连成多少条【只经过一个点的】直线。
显然,这个问题与 【1,i】与【1,j】里有多少对数互质这个问题是等价的。
可以递推
cnt[i][j]=cnt[i-1][j]+cnt[i][j-1]-cnt[i-1][j-1]+(gcd(i,j)==1?1:0);
其中gcd(i,j)==1时,表明i与j互质,则互质对数加1,可以根据容斥关系得到这个递推式
算完之后得到的是cnt[i][j]
然后我们求一遍二维前缀,就得到【i,j】矩阵内的所有斜边(单向)数量,也是用容斥关系递推,但是有个重复的问题是,例如点 (1,2)有x贡献值,那么待会点(2,4)的贡献y里面会包含x
而矩阵【i,j】的y贡献里重复的应该会是矩阵[i/2,j/2]的贡献x
因此最后 sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+cnt[i][j]-cnt[i/2][j/2];
答案乘2即可(对称
int N,M;
long long cnt[MAXN][MAXN],sum[MAXN][MAXN];
int gcd(int a,int b)
{
return a%b?gcd(b,a%b):b;
}
void init()
{
memset(cnt,0,sizeof(cnt));
memset(sum,0,sizeof(sum));
for(int i=1; i<MAXN; i++)
for(int j=1; j<MAXN; j++) cnt[i][j]=cnt[i-1][j]+cnt[i][j-1]-cnt[i-1][j-1]+(gcd(i,j)==1?1:0);
for(int i=1; i<MAXN; i++)
for(int j=1; j<MAXN; j++)
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+cnt[i][j]-cnt[i/2][j/2];
}
int main()
{
//freopen("in.txt", "r", stdin);
init();
while(scanf("%d%d",&N,&M),N||M)
{
printf("%lld\n",2*sum[N-1][M-1]);
}
return 0;
}
cnt
[
i
][
j
]=
cnt
[
i
-1
][
j
]+
cnt
[
i
][
j
-1
]-
cnt
[
i
-1
][
j
-1
]+(
gcd
(
i
,
j
)==
1
?
1
:
0
);