小K的矩阵
Description
小K得到了一个N * N的矩阵。这个矩阵最外圈都是1,每往里走一圈值会加1。小K想知道这个矩阵里所有数的和是多少(答案对1e9 + 7取模)。可以结合下面图片理解矩阵:
Input
输入第一行为一个T,代表测试样例组数,每组测试样例一个n,表示n * n的矩阵大小。
(1 <= T <= 100000,1 <= n <= 100000000)
Output
输出T行答案,每行答案一个整数,表示n * n矩阵内所有数的和模1e9 + 7的结果。
Sample Input 1
3
1
2
3
Sample Output 1
1
4
10
这是校内oj的题目,我一开始就只发现了每一项n的值为n-2项+n^2我就直接递推了
#include <queue>
#include <cstdlib>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int a[100000001];//大数放主函数外
int main()
{
a[1]=1;
a[2]=4;
for(int i=3;i<=100000000;i++)
a[i]=(a[i-2]+(i-2)*(i-2)+4*(i-1))%1000000007;
int t,n;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
printf("%d\n",a[n]);
}
return 0;
}
结果:数组开大了。。。
Memory Limit Exceeded
Time: 388ms Memory: 385MB Lang: C++
后来高人指点发现每一项n的值为n*(n+1)*(n+2)/6 这数学真妙
#include <queue>
#include <cstdlib>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
long long t,n;
scanf("%lld",&t);
while(t--)
{
scanf("%lld",&n);
printf("%lld\n",((n*(((n+1)*(n+2))%1000000007))%1000000007)/6);
}
return 0;
}
(n*(n+1)(n+2)/6)%1000000007或(n(((n+1)*(n+2)/6)%1000000007))%1000000007等,
结果:很迷惑。。。不过看出了亿点问题n最大1亿,
- n^3是24位直接溢出
- 两两相乘取模最后除6答案会不对,
- 两两相乘除6可能除不尽
Wrong Answer
不过教练说把6拆成2*3,3个连续的数判断一下总有能除尽3的,再找偶数除尽2的,好像有道理,
// if(n%3==0)
// {
// m=(n/3)*((n+1)/2)%1000000007;
// m=m*(n+2)%1000000007;
// }
// else if((n+1)%3==0)
// {
// m=((n+1)/3)*((n+2)/2)%1000000007;
// m=m*n%1000000007;
// }
// else
// {
// m=((n+1)/2)*((n+2)/3)%1000000007;
// m=m*n%1000000007;
// }
至于为什么 // 因为我没过,不知道错哪了
进入正题
正确做法是:逆元
唉,教练上课不听
现在自学
n*(n+1)%mod*(n+2)%mod/6%mod(错误的)<=>n*(n+1)%mod*(n+2)%modgetInv(6,mod)%mod(正确的)
逆元就是在mod意义下,不能直接除以一个数,而要乘以它的逆元。
比如a∗b≡1(modp),那么a,b互为模n意义下的逆元,比如你要算x/a,就可以改成xb%p
#include <queue>
#include <cstdlib>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define LL long long
const LL mod = 1e9+7;
LL exgcd(LL a,LL b,LL &x,LL &y)//扩展欧几里得算法
{
if(b==0)
{
x=1,y=0;
return a;
}
LL ret=exgcd(b,a%b,y,x);
y-=a/b*x;
return ret;
}
LL getInv(int a,int mod)//求a在mod下的逆元,不存在逆元返回-1
{
LL x,y;
LL d=exgcd(a,mod,x,y);
return d==1?(x%mod+mod)%mod:-1;
}
int main()
{
long long t,n,m;
scanf("%lld",&t);
while(t--)
{
scanf("%lld",&n);
m=n*(n+1)%mod*(n+2)%mod*getInv(6,mod)%mod;
printf("%lld\n",m);
}
return 0;
}