我正在解决一个问题,该问题表明我们有一个包含从1到N的整数的列表L。我们必须执行以下操作N-1次:
约束: 1≤N≤1,000,000
时间限制: 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 n;
cin>>n;
if(n == 1)
cout<<1<<endl;
else
{
ull modno = 1000000007;
ull ans = 1;
ull no = n+1;
while(no >= 1)
{
ans = (ans*no);
if(ans > modno)
ans = ans%modno;
no--;
}
ans = ans - 1;
ans = ans % modno;
cout<<ans<<endl;
答案 0 :(得分:3)
有一个总和的封闭式解决方案: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;
}
使用-O3
进行编译,volatile
只是为了确保编译器不会优化计算。
您的解决方案几乎相同,不到1秒。不知道要进一步优化什么。
答案 1 :(得分:0)
算法应如下所示:
总和<-1 对于指数<-2,n 总和=(总和+索引+总和*索引)mod 1000000007 结束于
说明:由于+和*是可交换和关联的,因此处理项目的顺序无关紧要,因此您在实现此循环方面做得很好,但不必要地使cal函数过于复杂。
其他答案告诉您计算((n + 1)!-1)mod modno,如果我们忘记模部分,这是正确的,但我怀疑计算((n + 1)!-1)mod无论n的值如何,modno都会产生与逐步计算结果相同的结果,因为每个步骤中都有+和*。如果其他回答者是正确的,则可以极大地优化算法。如果没有,那么优化起来就不那么容易了。
答案 2 :(得分:0)
问题仅显示“ Choose two elements of the list, let's denote them by X and Y.
”,并且没有说明需要选择元素的顺序。
因此可以将其重写为:
将列表分成每个CPU一个子列表
使用SIMD;为每个CPU的每一对计算(X+1)*(Y+1)
子列表并将结果存储为64位整数的新列表,因此
可以避免进行昂贵的模运算
使用SIMD;为中的每一对计算(X*Y - 1) % 1000000007
每个CPU的新子列表,并将结果存储为32位整数。
重复前面的2个步骤,直到剩下一个值
每个CPU(并在需要时执行最后的R = (R - 1) % 1000000007
以使其恢复为32位)。存放这些
值并终止一个列表中的所有线程。
使用SIMD;为每对计算(X+1)*(Y+1)
使用SIMD;为每对计算(X+*Y - 1) % 1000000007
重复前面的2个步骤,直到剩下一个值
答案 3 :(得分:0)
正如其他人提到的那样,问题归结为计算((n + 1)!-1)%p。您可以搜索有关执行此操作的快速方法(快速阶乘模质数)。 here
就是其中之一。更新:刚刚检查了Codechef的问题链接。与往常一样,诀窍在于您没有准确描述的约束条件。最多100000个案例,您必须执行相同的任务。由于n很小,因此可以使用标准for循环在1秒内获得单个fact(n)mod p。
不起作用的是为每个测试用例计算fact(n)mod p。像许多其他问题一样,您可以使用预计算受益:建立一个数组,其中arr [i]是i! mod p到i =最大值n可以取+1。使用此信息,您只需返回(arr [n + 1]-1)%p,就可以回答O(1)中的每个查询(测试用例)。
只需尝试一下并被接受。下次,请在您的描述中添加问题链接,通常情况是您认为某些问题不相关,而这部分是问题的整体答案。