C++ 计算第n个三角形数,它也是一个平方数

C++ 计算第n个三角形数,它也是一个平方数,c++,algorithm,dynamic-programming,matrix-multiplication,C++,Algorithm,Dynamic Programming,Matrix Multiplication,这个问题是在一次练习赛中出现的: 计算第n个三角形数,它也是一个平方数,模为10006699。(1 ≤ N≤ 10^18)最多有10^5个测试用例 我发现我可以用递推关系Ti=6Ti-1-Ti-2+2,T0=0,T1=1,很容易计算出来 我对每个测试用例使用了大约O(logn)性能的矩阵求幂,但显然太慢了,因为有10^5个测试用例。事实上,即使约束仅为(1),此代码也太慢≤ N≤ 10^6),在这里我可以进行O(N)预处理和O(1)查询 我应该改变解决问题的方法,还是只优化代码的某些部分 #in

这个问题是在一次练习赛中出现的:

计算第n个三角形数,它也是一个平方数,模为10006699。(1 ≤ N≤ 10^18)最多有10^5个测试用例

我发现我可以用递推关系Ti=6Ti-1-Ti-2+2,T0=0,T1=1,很容易计算出来

我对每个测试用例使用了大约O(logn)性能的矩阵求幂,但显然太慢了,因为有10^5个测试用例。事实上,即使约束仅为(1),此代码也太慢≤ N≤ 10^6),在这里我可以进行O(N)预处理和O(1)查询

我应该改变解决问题的方法,还是只优化代码的某些部分

#include <ios>
#include <iostream>
#include <vector>
#define MOD 10006699

/*
Transformation Matrix:

 0 1 0   t[i]     t[i+1]
-1 6 1 * t[i+1] = t[i+2]
 0 0 1     2        2
*/

std::vector<std::vector<long long int> > multi(std::vector<std::vector<long long int> > a, std::vector<std::vector<long long int> > b)
{
    std::vector<std::vector<long long int> > c(3, std::vector<long long int>(3));
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            for (int k = 0; k < 3; k++)
            {
                c[i][j] += (a[i][k] * b[k][j]) % MOD;
                c[i][j] %= MOD;
            }
        }
    }
    return c;
}

std::vector<std::vector<long long int> > power(std::vector<std::vector<long long int> > vec, long long int p)
{
    if (p == 1) return vec;
    else if (p % 2 == 1) return multi(vec, power(vec, p-1));
    else
    {
        std::vector<std::vector<long long int> > x = power(vec, p/2);
        return multi(x, x);
    }
}

int main()
{
    std::ios_base::sync_with_stdio(false);
    long long int n;
    while (std::cin >> n)
    {
        if (n == 0) break;
        else
        {
            std::vector<std::vector<long long int> > trans;
            long long int ans;
            trans.resize(3);

            trans[0].push_back(0);  
            trans[0].push_back(1);
            trans[0].push_back(0);
            trans[1].push_back(-1);
            trans[1].push_back(6);
            trans[1].push_back(1);
            trans[2].push_back(0);
            trans[2].push_back(0);
            trans[2].push_back(1);

            trans = power(trans, n);

            ans = (trans[0][1]%MOD + (2*trans[0][2])%MOD)%MOD;

            if (ans < 0) ans += MOD;

            std::cout << ans << std::endl;
        }
    }
}
#包括
#包括
#包括
#定义模块10006699
/*
变换矩阵:
0110T[i]t[i+1]
-1 6 1*t[i+1]=t[i+2]
0 0 1     2        2
*/
std::vector multi(std::vector a,std::vector b)
{
std::vector c(3,std::vector(3));
对于(int i=0;i<3;i++)
{
对于(int j=0;j<3;j++)
{
对于(int k=0;k<3;k++)
{
c[i][j]+=(a[i][k]*b[k][j])%MOD;
c[i][j]]=MOD;
}
}
}
返回c;
}
标准::向量幂(标准::向量向量,长整型p)
{
如果(p==1)返回向量;
否则如果(p%2==1)返回multi(vec,power(vec,p-1));
其他的
{
标准:向量x=功率(向量,p/2);
返回多(x,x);
}
}
int main()
{
std::ios_base::sync_with_stdio(false);
长整型n;
而(标准::cin>>n)
{
如果(n==0)中断;
其他的
{
std::向量转换;
长内特;
trans.resize(3);
trans[0]。推回(0);
trans[0]。推回(1);
trans[0]。推回(0);
trans[1]。将_向后推(-1);
trans[1]。推回(6);
trans[1]。推回(1);
trans[2]。推回(0);
trans[2]。推回(0);
trans[2]。推回(1);
trans=功率(trans,n);
ans=(反式[0][1]%MOD+(2*反式[0][2])%MOD)%MOD;
如果(ans<0)ans+=MOD;

std::cout注意:我删除了我的旧答案,这更有用

对于这个问题,您不太可能创建一个比O(logn)更好的渐近算法。但是,您可以对当前代码执行一些修改,这些修改不会改善渐近时间,但会提高性能

下面是对您的代码的修改,该修改产生了相同的答案:

#include <ctime>
#include <ios>
#include <iostream>
#include <vector>
#define MOD 10006699

void power(std::vector<std::vector<long long int> >& vec, long long int p)
{
    if (p == 1)
        return;

    else if (p & 1)
    {
        std::vector<std::vector<long long int> > copy1 = vec;
        power(copy1, p-1);

        std::vector<std::vector<long long int> > copy2(3, std::vector<long long int>(3));
        for (int i = 0; i < 3; i++)
            for (int j = 0; j < 3; j++)
            {
                for (int k = 0; k < 3; k++)
                    copy2[i][j] += (vec[i][k] * copy1[k][j]) % MOD;
                copy2[i][j] %= MOD;
            }
        vec = copy2;

        return;
    }

    else
    {
        power(vec, p/2);

        std::vector<std::vector<long long int> > copy(3, std::vector<long long int>(3));
        for (int i = 0; i < 3; i++)
            for (int j = 0; j < 3; j++)
            {
                for (int k = 0; k < 3; k++)
                    copy[i][j] += (vec[i][k] * vec[k][j]) % MOD;
                copy[i][j] %= MOD;
            }
        vec = copy;

        return;
    }
}

int main()
{
    std::ios_base::sync_with_stdio(false);
    long long int n;
    while (std::cin >> n)
    {
        std::clock_t start = std::clock();
        if (n == 0) break;

        std::vector<std::vector<long long int> > trans;
        long long int ans;
        trans.resize(3);

        trans[0].push_back(0);  
        trans[0].push_back(1);
        trans[0].push_back(0);
        trans[1].push_back(-1);
        trans[1].push_back(6);
        trans[1].push_back(1);
        trans[2].push_back(0);
        trans[2].push_back(0);
        trans[2].push_back(1);

        power(trans, n);

        ans = (trans[0][1]%MOD + (2*trans[0][2])%MOD)%MOD;
        if (ans < 0) ans += MOD;
        std::cout << "Answer: " << ans << std::endl;

        std::cout << "Time: " << (std::clock() - start) / (double)(CLOCKS_PER_SEC / 1000) << " ms" << std::endl;
    }
}

你能解释一下你是如何得出这个公式的吗:)?除非他找到一种方法在O(1)中实现它,否则我怀疑他会有比O(logn)更好的渐近性能。但是,我会改变while循环时钟,将O(1)操作包括在内,因为它们对小N有影响(编辑这个人被指示删除他的评论)我无法编辑我的评论,因此将其删除,顺便说一句,此建议不会改善时间复杂度,并可能导致溢出问题,因为它不断添加到
c
,而不对其应用Mod。OP说
Mod
10006699
,这与溢出
long-long int
相差甚远。它是pos(a[i][k]*b[k][j])
在通过模之前可能会溢出,但其中的3个加法不会溢出
long long int
将幂函数的方法从递归更改为迭代,避免创建新的
向量
可能会稍微改善这一点。有27个加法,但是的,您是对的,这不会导致溢出。@Phamtrong,你是对的,避免产生新的向量会产生很大的不同,我完全改变了我的代码,并将其放在上面的答案中。此外,尽管有27个加法,但模每3次迭代就会发生一次,这就是为什么我在溢出上下文中将其命名为3个加法
Alexanders-MBP:Desktop alexandersimes$ g++ before.cpp -O3 -o before
Alexanders-MBP:Desktop alexandersimes$ ./before 
1000000000000000000
Answer: 6635296
Time: 0.708 ms
1000000000000000000
Answer: 6635296
Time: 0.542 ms
1000000000000000000
Answer: 6635296
Time: 0.688 ms
1000000000000000000
Answer: 6635296
Time: 0.634 ms
1000000000000000000
Answer: 6635296
Time: 0.626 ms
1000000000000000000
Answer: 6635296
Time: 0.629 ms
1000000000000000000
Answer: 6635296
Time: 0.629 ms
1000000000000000000
Answer: 6635296
Time: 0.629 ms
1000000000000000000
Answer: 6635296
Time: 0.632 ms
1000000000000000000
Answer: 6635296
Time: 0.695 ms

Alexanders-MBP:Desktop alexandersimes$ g++ after.cpp -O3 -o after
Alexanders-MBP:Desktop alexandersimes$ ./after 
1000000000000000000
Answer: 6635296
Time: 0.283 ms
1000000000000000000
Answer: 6635296
Time: 0.287 ms
1000000000000000000
Answer: 6635296
Time: 0.27 ms
1000000000000000000
Answer: 6635296
Time: 0.27 ms
1000000000000000000
Answer: 6635296
Time: 0.266 ms
1000000000000000000
Answer: 6635296
Time: 0.265 ms
1000000000000000000
Answer: 6635296
Time: 0.266 ms
1000000000000000000
Answer: 6635296
Time: 0.267 ms
1000000000000000000
Answer: 6635296
Time: 0.21 ms
1000000000000000000
Answer: 6635296
Time: 0.208 ms