C++ 针对给定问题的优化算法?

C++ 针对给定问题的优化算法?,c++,optimization,C++,Optimization,我正在解决一个问题,这个问题表明我们有一个包含从1到N的整数的列表L。我们必须执行以下操作N−1次: 选择列表中的两个元素,让我们用X和Y表示它们 从L中删除所选元素 将数字X+Y+X*Y附加到L。 最后,L正好包含一个整数。找到这个整数。 因为答案可能很大,我们必须计算它的模10^9+7 限制条件: 1.≤N≤一百万 时限: 1秒 我已经写了这段代码,它在线性时间内给出了正确的答案,但它说这种方法超出了时间限制。有人能提供更好的优化解决方案吗 inline ull cal(ull x, ull

我正在解决一个问题,这个问题表明我们有一个包含从1到N的整数的列表L。我们必须执行以下操作N−1次:

  • 选择列表中的两个元素,让我们用X和Y表示它们
  • 从L中删除所选元素
  • 将数字X+Y+X*Y附加到L。 最后,L正好包含一个整数。找到这个整数。 因为答案可能很大,我们必须计算它的模10^9+7
  • 限制条件: 1.≤N≤一百万

    时限: 1秒

    我已经写了这段代码,它在线性时间内给出了正确的答案,但它说这种方法超出了时间限制。有人能提供更好的优化解决方案吗

    inline ull cal(ull x, ull y){
      ull ans, i, modno;
      modno = 1000000007;
      i = 1;
    
      ans = (x + y);
      i = (i*x) % modno;
      i = (i*y) % modno;
      ans = ans + i;
      ans = ans % modno;
      return ans;
    }
    
    int main(){
        ull n;
        cin>>n;
    
        ull sum, modno;
        sum = 0;
        modno = 1000000007;
    
        if(n == 1)
            cout<<1<<endl;
        else
        {
            sum = n + (n-1) + (n*(n-1));
            n -= 2;
            do
            {
                if(n <= 0)
                    break;
    
                sum = cal(sum, n);
                n -= 1;
            }while(1);
            cout<<ans<<endl;
         }
    
       return 0;
    }
    
    内联ull校准(ull x,ull y){
    乌兰斯,我,莫德诺;
    modno=100000007;
    i=1;
    ans=(x+y);
    i=(i*x)%modno;
    i=(i*y)%modno;
    ans=ans+i;
    ans=ans%modno;
    返回ans;
    }
    int main(){
    ulln;
    cin>>n;
    全和,莫德诺;
    总和=0;
    modno=100000007;
    如果(n==1)
    
    cout求和有一个封闭形式的解:
    L=(N+1)!-1

    求和遵循此循环方程
    L_N=N+L_(N-1)+N*L_(N-1),L_0=0
    ,只需始终选择
    X=L_(N-1)
    Y=N
    (=要添加的下一个数字)即可获得

    派生:

    编辑:

    当您发布最终代码时,我将发布我的基准:

    #include <iostream>
    #include <cstdint>
    #include <chrono>
    
    std::uint64_t
    factorial(std::uint64_t n) {
        std::uint64_t x = 1;
        while (n > 1)
            x = (x * n--) % 1'000'000'007;
        return x;
    }
    int
    main() {
        std::uint64_t n;
        std::cin >> n;
        std::uint64_t numMicro = 0;
        for (std::size_t i = 0; i < 1'000; ++i) {
            auto start = std::chrono::high_resolution_clock::now();
            volatile std::uint64_t res = factorial(n);
            auto end = std::chrono::high_resolution_clock::now();
            numMicro +=
                std::chrono::duration_cast<std::chrono::microseconds>(end - start)
                    .count();
        }
        std::cout << "On average: " << numMicro / 1000.0 << "microseconds";
        return 0;
    }
    
    #包括
    #包括
    #包括
    标准:uint64\U t
    阶乘(标准::uint64\u t n){
    标准:uint64_t x=1;
    而(n>1)
    x=(x*n--)%1'000'000'007;
    返回x;
    }
    int
    main(){
    标准:uint64\u t n;
    标准:cin>>n;
    标准:uint64\u t numMicro=0;
    对于(标准:尺寸i=0;i<1'000;++i){
    自动启动=标准::时钟::高分辨率时钟::现在();
    挥发性标准:uint64_t res=阶乘(n);
    自动结束=标准::时钟::高分辨率时钟::现在();
    努米克罗+=
    标准::计时::持续时间(结束-开始)
    .count();
    }
    
    std::cout算法应该如下所示:

    sum问题只是说“
    选择列表中的两个元素,让我们用X和Y表示它们。
    没有说明元素需要选择的顺序

    因此,可以将其改写为:

  • 将列表拆分为每个CPU的一个子列表

  • 使用SIMD;为每个CPU中的每对计算
    (X+1)*(Y+1)
    子列表并将结果作为64位整数存储在新列表中 可以避免进行昂贵的模运算

  • 使用SIMD;计算中每对的
    (X*Y-1)%100000007
    每个CPU的新子列表,并将结果存储为32位整数

  • 重复前面的2个步骤,直到剩下一个值 每个CPU(如果需要将其恢复到32位,则执行最后的
    R=(R-1)%100000007
    ) 值,并终止除一个线程以外的所有线程

  • 使用SIMD;计算每对的
    (X+1)*(Y+1)

  • 使用SIMD;计算每对的
    (X+*Y-1)%100000007

  • 重复前面的2个步骤,直到只剩下一个值


  • 正如其他人所提到的,这个问题归结为计算((n+1)!-1)%p。你可以四处搜索做这件事的快速方法(快速阶乘模素数)。在1s下工作的方法之一就是上面提到的方法

    更新:刚刚检查了codechef中的问题链接。与往常一样,诀窍在于您没有准确描述的约束。您必须对多达100000个案例执行相同的任务。使用standard for loop可以在1秒内获得单个事实(n)mod p,因为n很小

    不起作用的是为每个测试用例计算事实(n)mod p。与许多其他问题一样,使用预计算可以从中受益:构建一个数组,其中arr[i]是i!mod p到i=最大值n可以取+1。有了这些信息,您可以通过返回(arr[n+1]-1)%p来回答O(1)中的每个查询(测试用例)


    下一次,请在你的描述中添加问题链接,通常情况下,你认为某件事与问题无关,而这一部分就是问题的全部答案。

    考虑一下,如果在3)中你只需要使用
    X+Y
    ,那么解决方案是什么,然后尝试以小步骤进行处理(例如,
    X+Y+c
    对于某些常量
    c
    ,什么样的解决方案?)性能提升功能更适合于“”。要问的问题是,万一有人对OP描述中遗漏的条件有点好奇。添加的最终代码可能会重复,这也会超出时间限制。有人能为这个问题提供一个有效的实现吗?我也尝试过像威尔逊定理这样的快速阶乘算法。也许是真的ds计算阶乘的另一种方法我仍然需要为每次迭代计算它模10^9+7,因为数字很大,这再次超过了时间限制。对此有什么建议吗?@Sam我非常怀疑有更紧凑的解决方案,但由于10^9+7是素数,这个问题适用。@Sam你介意在你的问题中添加最后的代码吗在上,因为我对N=1e6的朴素阶乘实现只需要~4ms。@Sam发布了我的基准测试,我恐怕做不到比这个更好的答案。你能提供一个实现吗?因为我的实现似乎给出了一些错误。即使使用快速方法,时间限制也超过了,可能有不同的方法,如果你需要,请帮助我tsolution@sam你试过哪一个?我试过使用威尔逊定理,但仍然超出了时间限制。无法实现你评论中给出的那些,如果你能提供实现,那将是一个很大的帮助。谢谢
    #include <iostream>
    #include <cstdint>
    #include <chrono>
    
    std::uint64_t
    factorial(std::uint64_t n) {
        std::uint64_t x = 1;
        while (n > 1)
            x = (x * n--) % 1'000'000'007;
        return x;
    }
    int
    main() {
        std::uint64_t n;
        std::cin >> n;
        std::uint64_t numMicro = 0;
        for (std::size_t i = 0; i < 1'000; ++i) {
            auto start = std::chrono::high_resolution_clock::now();
            volatile std::uint64_t res = factorial(n);
            auto end = std::chrono::high_resolution_clock::now();
            numMicro +=
                std::chrono::duration_cast<std::chrono::microseconds>(end - start)
                    .count();
        }
        std::cout << "On average: " << numMicro / 1000.0 << "microseconds";
        return 0;
    }