2018 Multi-University Training Contest 7 1010 Sequence

Problem Description

Let us define a sequence as below
 

\left\{\begin{matrix} & & \\ F1=A & & \\ F2=B & & \\ Fn=C*F_{n-2}+D*F_{n-1}+\left \lfloor P/n \right \rfloor & & \end{matrix}\right.


  Your job is simple, for each task, you should output Fn module 109+7.

 

Input

The first line has only one integer T, indicates the number of tasks.

Then, for the next T lines, each line consists of 6 integers, A , B, C, D, P, n.

1≤T≤200≤A,B,C,D≤1091≤P,n≤109

 

Sample Input

2 3 3 2 1 3 5 3 2 2 2 1 4

 

Sample Output

36 24

 

题解:

题目给一个递推式,求Fn,很容易构造出递推矩阵

\begin{bmatrix} D & C &\left \lfloor p/n \right \rfloor \\ 1& 0 & 0\\ 0& 0 & 1 \end{bmatrix} * \begin{bmatrix} F_{n-1}\\ F_{n-2}\\ 1 \end{bmatrix} = \begin{bmatrix} F_{n}\\ F_{n-1}\\ 1 \end{bmatrix}

设3*3阶矩阵为Kn。

因为\left \lfloor {p/n} \right \rfloor的值可能不同,所以需要分段求快速幂

例如p=100,n=3和n=4时\left \lfloor {p/n} \right \rfloor的不同,所以F4=F3*K4

而n∈[51,100]时,\left \lfloor {p/n} \right \rfloor的值都相等,所以F_{100}=F_{50}*(K_{100})^{^{50}},即分段后再求快速幂

\left \lfloor {p/n} \right \rfloor分段代码如下:

 for (int i = 1;i <= p;i++) {
        tmp1 = p / i;
        if (tmp2 == tmp1)break;
        kop[num++] = tmp1;
        tmp2 = tmp1;
  }
for (int i = tmp2 - 1;i > 0;i--)
        kop[num++] = i;

大意是从大到小求使\left \lfloor {p/n} \right \rfloor不同的n值,如果\left \lfloor p/i \right \rfloor的值重复,则结束循环,后面的值依次递减即可。

下面贴出完整代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int Matr = 4;
const ll mod = 1e9 + 7;
const int mmax = 1e5 + 7;
int kop[mmax];
struct mat
{
	ll a[Matr][Matr], size;
	mat(){
		size = 0;
		memset(a, 0, sizeof(a));
	}
};
/*
void print(mat m)
{
	int i, j;
	printf("%d\n", m.size);
	for (i = 1;i <= m.size;i++)
	{
		for (j = 1;j <= m.size;j++)printf("%d ", m.a[i][j]);
		printf("\n");
	}
}
*/

mat multi(mat m1, mat m2)
{
	mat ans = mat();    ans.size = m1.size;
	for (int i = 1;i <= m1.size;i++)
		for (int j = 1;j <= m2.size;j++)
			if (m1.a[i][j])
				for (int k = 1;k <= m1.size;k++)
					ans.a[i][k] = (ans.a[i][k] + m1.a[i][j] * m2.a[j][k]) % mod;
	return ans;
}
mat quickmulti(mat m, mat op, int n)
{
	mat ans = mat();
	int i;
	for (i = 1;i <= m.size;i++)
		for (int j = 1;j <= 3;j++)
			ans.a[i][j] = op.a[i][j];
	ans.size = m.size;
	while (n)
	{
		if (n & 1)ans = multi(m, ans);
		m = multi(m, m);
		n >>= 1;
	}
	return ans;
}
int main()
{
	ll t, a, b, c, d, p, n, i;
	cin >> t;
	while (t--)
	{
		cin >> a >> b >> c >> d >> p >> n;
		if (n <3)
		{
			cout << (n==1?a:b) << endl;
			continue;
		}
		mat k, op;
		k.a[1][1] = d;
		k.a[1][2] = c;
		k.a[1][3] = p;
		k.a[3][3] = k.a[2][1] = 1;
		k.a[2][2] = k.a[2][3] = k.a[3][1] = k.a[3][2] = 0;
		k.size = 3;
		for (int i = 1;i <= 3;i++)
			op.a[i][i] = 1;
		op.size = 3;

		ll num = 0, tmp1, tmp2 = 0;
		for (int i = 1;i <= p;i++)   //计算分段的n值
		{
			tmp1 = p / i;
			if (tmp2 == tmp1)break;
			kop[num++] = tmp1;
			tmp2 = tmp1;
		}
		for (int i = tmp2 - 1;i > 0;i--)
			kop[num++] = i;
		kop[num] = 0;

		while (kop[num - 1] < 3)   //n从3开始算起
			num--;
		kop[num] = 2;
		//for (int i = 0;i < num;i++)
		//    cout << kop[i] << " ";
		//cout << endl;

		for (i = num - 1;i >= 0;i--)
		{
			if (n < kop[i])break;
			k.a[1][3] = p / kop[i];
			op = quickmulti(k, op, kop[i] - kop[i + 1]);
		}
		k.a[1][3] = p / n;
		op = quickmulti(k, op, n - kop[i + 1]);
		cout << ((op.a[1][1] * b + op.a[1][2] * a + op.a[1][3]) % mod) << endl;

	}
	//system("pause");
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值