C++ NxM网格上的平行四边形数

C++ NxM网格上的平行四边形数,c++,algorithm,mathematical-lattices,C++,Algorithm,Mathematical Lattices,我必须解决一个问题,当给定一个网格大小nxm时,我必须找到可以放入其中的平行四边形的数量,这样它们每个坐标都是一个整数 这是我的密码: /* ~Keep It Simple!~ */ #include<fstream> #define MaxN 2005 int N,M; long long Paras[MaxN][MaxN]; // Number of parallelograms of Height i and Width j long long Rects;

我必须解决一个问题,当给定一个网格大小nxm时,我必须找到可以放入其中的平行四边形的数量,这样它们每个坐标都是一个整数

这是我的密码:

/*
      ~Keep It Simple!~
*/

#include<fstream>

#define MaxN 2005

int N,M;
long long Paras[MaxN][MaxN]; // Number of parallelograms of Height i and Width j
long long Rects; // Final Number of Parallelograms

int cmmdc(int a,int b)
{
while(b)
{
    int aux = b;
    b = a -(( a/b ) * b);
    a = aux;
}

return a;
}

int main()
{
freopen("paralelograme.in","r",stdin);
freopen("paralelograme.out","w",stdout);

scanf("%d%d",&N,&M);

for(int i=2; i<=N+1; i++)
    for(int j=2; j<=M+1; j++)
    {
        if(!Paras[i][j])
          Paras[i][j] = Paras[j][i] = 1LL*(i-2)*(j-2) + i*j - cmmdc(i-1,j-1) -2; // number of parallelograms with all edges on the grid + number of parallelograms with only 2 edges on the grid.
        Rects += 1LL*(M-j+2)*(N-i+2) * Paras[j][i]; // each parallelogram can be moved in (M-j+2)(N-i+2) places.
    }

printf("%lld", Rects);
}
示例:对于2x2网格,我们有22个可能的平行四边形

我的算法是有效的,它是正确的,但我需要让它快一点。我想知道怎么可能


另外,我听说我应该预处理最大公约数,并将其保存在一个数组中,这样可以将运行时间减少到On*m,但我不知道如何在不使用cmmdc最大公约数函数的情况下做到这一点。

确保N不小于m:

if( N < M ){ swap( N, M ); }
替换此行:b=a-a/b*b;使用余数运算符:

b = a % b;
缓存cmmdc结果可能是可能的,您可以使用某种筛选算法初始化数组:创建一个由a和b索引的2d数组,在a和b是2的倍数的每个位置放置2,然后在a和b是3的倍数的每个位置放置3,依此类推,大致如下:

int gcd_cache[N][N];

void init_cache(){
    for (int u = 1; u < N; ++u){
        for (int i = u; i < N; i+=u ) for (int k = u; k < N ; k+=u ){
            gcd_cache[i][k] = u;
        }
    }
}

但是,不确定它是否有很大帮助。

代码中的第一条注释保持简单,因此,鉴于此,为什么不尝试用数学方法解决问题并打印结果呢

如果从栅格中选择两条长度为N的直线,则可以通过以下方式找到平行四边形的数量:

选择两条线中相邻的两个点:有N-1^2 这样做的方法,因为您可以在N-1上定位两点 每行上的位置。 在两条直线上选择两个点,两个点之间有一个空格:有N-2^2种方法。 选择两个点,两个点之间有两个、三个和最多N-2个空间。 结果组合数为N-1^2+N-2^2+N-3^2+…+1。 通过求和,我们得到了公式:1/6*N*2*N^2-3*N+1。查证。 现在有了两行的解,只需将它乘以M的2阶数,即M/2*M-2

因此,整个公式是:1/12*N*2*N^2-3*N+1*M/M-2!,在哪里!Mark表示,并且^表示幂算符,注意到相同符号不是C++中的幂运算符,而是位异或XOR运算符。
此计算所需的操作比遍历矩阵所需的操作要少。

Hi。我敢肯定你的计算机科学老师只是问你这个问题,让你知道这个问题有多难。你可能会学到的下一件事是基于晶格问题的密码学,也就是量子计算机无法打破的下一件大事:-不是我的计算机科学老师问我问题,我的知识可能是我的计算机科学老师的十倍。我只是在为我的国家的算法奥林匹克做准备。我确实用数学方法解决了它,上面有两个公式,我需要它在0.4秒内在2000x2000网格上运行。Keep it simple标记是我在我制作的每个程序中添加的东西,您的前两个想法似乎会加快速度,但会产生半正确的源代码。根据我第一次发布的消息来源,我得到了所有测试的正确答案,除了一个,这是一个时间限制。有了你的资料,我只有50%的测试是正确的,都在时间范围内。我已经找到了原因并编辑了答案,事情是t1对于这两种情况是不一样的。但是这个解决方案仍然没有停留在时间限制内。
b = a % b;
int gcd_cache[N][N];

void init_cache(){
    for (int u = 1; u < N; ++u){
        for (int i = u; i < N; i+=u ) for (int k = u; k < N ; k+=u ){
            gcd_cache[i][k] = u;
        }
    }
}