题意:
给n*m的点阵,问有多少条线通过至少两个点,且不竖直,不水平。
题解:
首先,由于\这个斜线的数目和/的数目是相同的,所以我们只需计算\的数目然后×2即可。
用d[i][j]保存从左上角(0,0)到(i,j)这个矩形中从(0,0)出发的线总数,可以发现当gcd(i,j)==1时,从(0,0)到(i,j)这条直线在该矩形中没有重复。
所以d[i][j]=d[i-1][j]+d[i][j-1]-d[i-1][j-1]+(gcd(i,j)==1);
那么我们求出左上角(0,0)到(i,j)这个矩形内从(0,0)出发的线数有什么用呢?
接下来我们用pre[i][j]来保存从(0,0)到(i,j)这个矩形中所有的可行线数目,我们发现每多一个新的点(i,j),pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+(经过(i,j)且在(0,0)到(i,j)这个矩形内的可行线数目)。
那么这个数目怎么求呢?我们先前d[i][j]保存的是从(0,0)出发,其实从(0,0)出发等价于从(i,j)出发,因为矩形相同。
所以pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+d[i][j]。。?其实不是,还有重复,什么情况会重复呢?从(i,j)出发的线是否在之前就已经算在之内了呢?
我们可以发现:线重复的充要条件是(从(i,j)出发的线至少经过了该矩形中另外两个点)
那么怎么去掉呢?
我们发现这种线的数目必定是在d[i/2][j/2]里面,因为线的长度起码要乘2才会经过另外一个点,并且d[i/2][j/2]中所有的线都会重复,因为里面线的长度都起码可以×2,而只要长度×一个大于1的数,他必定可以经过另外一个点。而先前gcd(i,j)==1这个条件,是让线在d[i][j]中没有重复。
所以最终pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+d[i][j]-d[i/2][j/2];
#include <iostream>
#include <string>
#include <cstring>
#include <cstdio>
#include <queue>
#include <map>
#include <stack>
#include <algorithm>
#include <vector>
#include <cmath>
#include <set>
#include <iomanip>
#include <sstream>
#include <ctime>
#define dep(a,b,c) for(int a=b;a>=c;a--)
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define mem(a,b) memset(a,b,sizeof(a))
#define IO ios::sync_with_stdio(false)
#define pb push_back
#define debug(x) cout<<"["<<x<<"]"<<endl
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3f;
const int MOD = 1e9+7;
const int MAXN= 3e2+5;
int d[MAXN][MAXN];
int pre[MAXN][MAXN];
int main() {
int n,m;
rep(i,1,300)
rep(j,1,300)
d[i][j]=d[i-1][j]+d[i][j-1]-d[i-1][j-1]+(__gcd(i,j)==1);
rep(i,1,300)
rep(j,1,300)
pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+d[i][j]-d[i/2][j/2];
while(~scanf("%d%d",&n,&m)){
if(!n&&!m)break;
printf("%d\n",pre[n-1][m-1]*2);
}
}