前言
又是一个只过了签到题的比赛,蒟蒻又来补题了。

题目链接
https://ac.nowcoder.com/acm/contest/1114/B

题目理解
蒟蒻看到这一题就想用暴力做,无奈n很大,暴力只会超时。做的时候想过将
∑
i
=
1
n
\sum_{i=1}^{n}
∑i=1n
∑
j
=
1
i
\sum_{j=1}^{i}
∑j=1i变换一下,但是蒟蒻太菜了,于是愉快的等到比赛结束去看题解。题解里果然是交换成
∑
j
=
1
n
\sum_{j=1}^{n}
∑j=1n
∑
i
=
j
n
\sum_{i=j}^{n}
∑i=jn,为什么可以这样交换可以看下面的图。把两个式子看成同一个下三角矩阵就行了,两个是一摸一样的。

- 对原式 ∑ i = 1 n ∑ j = 1 i i ∗ ( ⌈ i j ⌉ ) j \sum_{i=1}^{n}\sum_{j=1}^{i}i\ast (\left \lceil \frac{i}{j} \right \rceil)^{j} ∑i=1n∑j=1ii∗(⌈ji⌉)j来说, j在内循环而且它还是指数,很不好写程序,要考虑的东西很多。但是交换一下顺序就不同了。
- 换成 ∑ j = 1 n ∑ i = j n i ∗ ( ⌈ i j ⌉ ) j \sum_{j=1}^{n}\sum_{i=j}^{n}i\ast (\left \lceil \frac{i}{j} \right \rceil)^{j} ∑j=1n∑i=jni∗(⌈ji⌉)j后,j在外层,那么对于内循环来说只需要考虑i作为变量,把i与 ( ⌈ i j ⌉ ) j (\left \lceil \frac{i}{j} \right \rceil)^{j} (⌈ji⌉)j分开计算即可。
- 考虑n很大,可以采用更快速的方法,这里我们找 ( ⌈ i j ⌉ ) j (\left \lceil \frac{i}{j} \right \rceil)^{j} (⌈ji⌉)j的规律。
打表如下

可以看出
(
⌈
i
j
⌉
)
j
(\left \lceil \frac{i}{j} \right \rceil)^{j}
(⌈ji⌉)j可以把n分成
n
j
\frac{n}{j}
jn块,然后每一块的分别算幂,用快速幂也会超时,所以需要用数组模拟每一块的幂。对于计算i可以用每一块长度的等差求和公式来计算。但是对于长度不满
n
j
\frac{n}{j}
jn的块来说,需要处理一下单独的长度就行了。
AC代码
//思维+分块+模拟快速幂+取模+数列公式
#include<iostream>
#define int long long
using namespace std;
const int maxn = 3e7 +10;
const int mod = 1e9+7;
signed main(){
int n;
scanf("%lld",&n);
int prime[maxn];
fill(prime,prime+n+1,1);
int ans=0;
for(int j=1;j<=n;j++){
int k=n/j;
int l=j;
int r=j+j-1;
for(int i=1;i<k;i++){
prime[i]=1LL*prime[i]*i%mod;
ans=(ans+(1LL*((r+l)*j/2%mod)*prime[i]%mod))%mod;
l+=j;
r+=j;
}
prime[k]=1LL*prime[k]*k%mod;
ans=(ans+1LL*(l+n)*(n-l+1)/2%mod*prime[k]%mod)%mod;
}
printf("%lld",ans);
return 0;
}
本文分享了一道ACM竞赛题目的解题思路,通过巧妙的数学变换优化算法,将原本难以解决的问题转化为易于编程的形式。文章详细解释了如何通过交换求和顺序,将复杂度降低,并利用快速幂和数列公式进行高效计算。
1152

被折叠的 条评论
为什么被折叠?



