ZOJ 3903 Ant (数学)

题目链接: http://www.icpc.moe/onlinejudge/showProblem.do?problemId=5630


一道数学题。可惜我从小数学就不好...

首先是公式推导,参考博客:http://blog.csdn.net/bigbigship/article/details/49123643   感谢!

具体是这样:

设最长的边为 n ,最短的边为 x ,次短的边为 y  , 
ΣL2=nx=1ny=xn2+(x+y)2  
ΣL2=n(n+1)/2n2+nx=1ny=xx2+y2+2xy  
Sum1=nx=1ny=x2xy  
Sum1=nx=12xny=xy  
Sum1=nx=12x(x+n)(nx+1)/2  
Sum1=nx=1x3+x2+(n2+n)x  
Sum2=nx=1ny=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)(2n+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;
}


你可能感兴趣的:(ZOJ 3903 Ant (数学))