小K的矩阵

小K的矩阵

Description

小K得到了一个N * N的矩阵。这个矩阵最外圈都是1,每往里走一圈值会加1。小K想知道这个矩阵里所有数的和是多少(答案对1e9 + 7取模)。可以结合下面图片理解矩阵:
n=4

n=5

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亿,

  1. n^3是24位直接溢出
  2. 两两相乘取模最后除6答案会不对,
  3. 两两相乘除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,就可以改成x
b%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;
}

求逆元

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值