2019沈阳网络赛 问题K Guanguan's Happy water (高斯消元+矩阵快速幂 / 瞎搞)

本文详细解析了一道数学编程题目,通过矩阵快速幂和高斯消元的方法求解线性方程组,同时提供了一种简化算法实现O(1)计算复杂度的解法。文章分享了作者的解题思路与代码实现,对比了两种算法的优劣,并附带了完整的代码示例。
摘要由CSDN通过智能技术生成

原题链接

题意:第一行给出k,n,第二行2*k个数字,分别是a1,a2…ak 以及 f1,f2…fk
已知满足
f1 = a1 * p1 + a2 * p2 + a3 * p3…+ ak * pk
f2 = a2 * p1 + a3 * p2 + a4 * p3…+ f1 * pk

fk = ak * p1 + f1 * p2 + f2 * p3…+ fk-1 * pk

以此类推的k个式子,求f的前n项和

思路:正解应该是高斯消元+矩阵快速幂,求出p1-pk,但是高斯消元过程中可能会出现小数,利用费马小定理求出逆元,然后矩阵快速幂进行计算。。但是后来完事以后看网上题解发现好多人水过了,,好像是f在第k项以后就不会变了,一直是同一个数,那么直接读入完就能o(1)算出来,,吐血,,我说怎么那么多人过呢

当时没想过这题能这么解,,也没想到高斯消元小数要怎么处理,,,矩阵快速幂写的也不利索,枯了。。太菜了还是

1.矩阵快速幂做法:

/*
构造出的初始矩阵是: ak, ak-1, ... a1, ans
每次要乘的base矩阵是: 
pk    1  0  0   ... 1
pk-1  0  1  0   ...
pk-2  0  0  1   ... 
...
p1    0  0  0   ... 0
0     0  0  0   ... 1
*/
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
const int N = 100,mod=1e9+7;
ll a[N][N],tem[N*2];
ll n,k;
struct mat
{
	ll a[N][N];
};
ll q_pow(ll a,ll b)
{
	ll	ans=1;
	while(b)
	{
		if(b&1)
			ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}
void gauss()
{
    int c,r,n=k;
    for(c=0,r=0;c<n;c++)
    {
        int t=r;
        for(int i=r+1;i<n;i++)
            if(fabs(a[i][c])>fabs(a[t][c]))
                t=i;
        for(int j=c;j<=n;j++) 
            swap(a[t][j],a[r][j]);
        for(int j=n;j>=c;j--) 
			a[r][j]=a[r][j]*q_pow(a[r][c],mod-2),a[r][j]%=mod;
        for(int i=r+1;i<n;i++)
        	for(int j=n;j>=c;j--)
                    a[i][j]-=a[r][j]*a[i][c],a[i][j]%=mod;              
        r++;
    }
    for(int i=n-1;i>=0;i--)
    	for(int j=i+1;j<n;j++)
            a[i][n]-=a[j][n]*a[i][j],a[i][n]%=mod;   
	
}
mat mul(mat a,mat b)
{
	mat ans;
	memset(ans.a,0,sizeof ans.a);
	for(int i=0;i<=k;i++)
		for(int j=0;j<=k;j++)
			for(int r=0;r<=k;r++)
			{
				ans.a[i][j]+=a.a[i][r]*b.a[r][j]%mod;
				ans.a[i][j]%=mod;
			}
	return ans;
}
mat mul_pow(mat ans,mat a,ll b)
{
	while(b)
	{
		if(b&1)
			ans=mul(ans,a);
		a=mul(a,a);
		b>>=1;
	}
	return ans;
}
ll solve()
{
	mat base,ans;
	memset(base.a,0,sizeof base.a);
	memset(ans.a,0,sizeof ans.a);
	for(int i=0;i<k;i++)
		ans.a[0][i]=tem[k-i];
	ll sum=0;
	for(int i=1;i<k;i++)
		sum=(sum+tem[i])%mod;
	ans.a[0][k]=sum;
	for(int i=0;i<k;i++)
		base.a[i][0]=a[k-i-1][k];
	for(int i=0;i<k-1;i++)
		base.a[i][i+1]=1;
	base.a[0][k]=base.a[k][k]=1;
	ans=mul_pow(ans,base,n-k+1);			
	return ans.a[0][k]%mod;
}
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		cin>>k>>n;
		for(int i=1;i<=2*k;i++)
			scanf("%lld",&tem[i]);
		if(n<=2*k)
		{
			ll sum=0;
			for(int i=1;i<=n;i++)
				sum=(sum+tem[i])%mod;
			cout<<sum<<endl;continue;
		}
		for(int i=0;i<k;i++)
			for(int j=0;j<=k;j++)
				a[i][j]=tem[i+j+1];
		gauss();
		cout<<solve()<<endl;
	}
	return 0;
}


2.瞎搞做法:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		ll n,k,f,sum=0,x;
		cin>>k>>n;	
		for(int i=1;i<=k;i++)
			cin>>x,sum=(sum+x)%mod;
		for(int i=1;i<=k;i++)
			cin>>x;
		ll ans=(sum+(n-k)%mod*x%mod)%mod;
		cout<<ans<<endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值