题目链接: http://www.icpc.moe/onlinejudge/showProblem.do?problemId=5630
一道数学题。可惜我从小数学就不好...
首先是公式推导,参考博客:http://blog.csdn.net/bigbigship/article/details/49123643 感谢!
具体是这样:
设最长的边为 n ,最短的边为 x ,次短的边为 y ,
ΣL2=∑nx=1∑ny=xn2+(x+y)2
ΣL2=n∗(n+1)/2∗n2+∑nx=1∑ny=xx2+y2+2∗x∗y
设 Sum1=∑nx=1∑ny=x2∗x∗y
Sum1=∑nx=12∗x∑ny=xy
Sum1=∑nx=12∗x∗(x+n)∗(n−x+1)/2
Sum1=∑nx=1−x3+x2+(n2+n)∗x
设 Sum2=∑nx=1∑ny=xx2+y2 由于 x,y 的取值范围都为 [1,n] ,并且每个数的平方都恰好出现了 n+1 次
因此 Sum2=∑nx=1x2∗(n+1)
ΣL2=Sum1+Sum2+n*(n+1)/2=∑nx=1(−x3+(n+2)∗x2+(n2+n)∗x) +n*(n+1)/2
补充三个求和公式:
∑nx=1x=n∗(n+1)/2
∑nx=1x2=n∗(n+1)∗(2∗n+1)/6
∑nx=1x3=[n∗(n+1)/2]2
另外我这个小白从这一题学会了利用费马小定理求逆元,参考博客:https://www.huangwenchao.com.cn/2014/05/mod-div.html 感谢!
带模的除法可以转换成带模乘法,带模除法是不满足同余定理的。
用费马小定理求逆元的意思是,带模的除法:求 a / b = x (mod M)
只要 M 是一个素数,而且 b 不是 M 的倍数,就可以用一个逆元整数 b’,通过 a / b = a * b' (mod M)
,来以乘换除。
费马小定理说,对于素数 M 任意不是 M 的倍数的 b,都有:
b ^ (M-1) = 1 (mod M)
于是可以拆成:
b * b ^ (M-2) = 1 (mod M)
于是:
a / b = a / b * (b * b ^ (M-2)) = a * (b ^ (M-2)) (mod M)
也就是说我们要求的逆元就是 b ^ (M-2) (mod M)
这样就只需写一个快速幂来求2,、4、6的逆元即可。
数学好伟大。。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> using namespace std; typedef long long ll; const ll m = 1000000007; ll qpow(ll a, ll b) { ll res = 1; while(b) { if(b & 1) res = res * a % m; a = a * a % m; b >>= 1; } return res; } int main() { ll inv2 = qpow(2ll, m - 2); ll inv4 = qpow(4ll, m - 2); ll inv6 = qpow(6ll, m - 2); int T; scanf("%d", &T); while(T--) { ll n; scanf("%lld", &n); n %= m; ll ans = 0; ans = (n + 1) % m * n % m * inv2 % m * n % m * n % m; ans = (ans - n * n % m * (n + 1) % m * (n + 1) % m * inv4 % m + m) % m; ans = (ans + (n + 2) % m * n % m * ((n + 1) % m) % m * ((2 * n + 1) % m) % m * inv6 % m) % m; ans = (ans + (n * n + n) % m * n % m * (n + 1) % m * inv2 % m) % m; printf("%lld\n", ans); } return 0; }