中国(北方)大学生程序设计训练赛(第一周)E. water problem
2017-03-11 16:31
267 查看
函数 f:Z+→Zf:Z+→Z。已知 f(1),f(2)f(1),f(2) 的值,且对于任意 x>1x>1,有 f(x+1)=f(x)+f(x−1)+sin(πx2)f(x+1)=f(x)+f(x−1)+sin(πx2)。
求 f(n)f(n的值
多组数据。(数据组数 T≤100T≤100)
每组数据包含 33 个不超过 109109 的正整数,分别代表 f(1),f(2)f(1),f(2) 和 nn 的值。
输出 f(n)mod(109+7)f(n)mod(109+7)。每组输出末尾有换行符。
题意:
题目类似斐波那契数列,但比斐波那契数列多了一项。
思路:
首先回忆斐波那契数列的计算方法:
一、递归
直接按照递推公式递归运算,此方法有大量重复计算,导致n趋近不足100就无法在有限时间内计算出来,通常作为递归讲解的例子。
二、动态规划(dp)
为了避免重复计算,我们通常用一个数组记录已经计算的值,采用记忆化搜索的动态规划,就可以减少重复计算。
根据dp
= dp[n-1] + dp[n-2] 从第三项开始迭代即可。
此方法的时间复杂度为O(n),线性时间复杂度,考虑在1s内,无法计算大于1e8的数据范围。
三、矩阵快速幂
数列的递推公式为:f(1)=1,f(2)=2,f(n)=f(n-1)+f(n-2)(n>=3)
用矩阵表示为:
进一步,可以得出直接推导公式:
所以,对矩阵[1,1,1,0]进行n-2次幂运算,就可以计算出f(n)的值,此时由于幂运算可以使用快速幂运算,可以用矩阵快速幂来进行计算。
此时的时间复杂度为O(logN)
对比斐波那契数列,将其推广
由于本题中的递推关系非线性,即有sin项的约束,f(x+1)=f(x)+f(x−1)+sin(πx2)f(x+1)=f(x)+f(x−1)+sin(πx2)。我们展开两项,得到f(x)
= f(x-1) + f(x-3) + f(x-4),通过sin的周期性,消去了常数项。
得到了一个四阶线性递推公式。
类比斐波那契数列的方法,我们需要找一个转移矩阵,满足上述矩阵运算。
由于斐波那契数列是二阶递推数列,所以存在一个2*2的矩阵A,使得:
[Fn Fn-1] = [Fn-1 Fn-2] * A
所以以上的四阶递推公式也一定存在一个4*4的矩阵A,满足
[Fn Fn-1 Fn-2 … Fn-k+1] = A*[Fn-1 Fn-2 Fn-3 …
Fn-k]
= …
= An-k+1 *[Fk-1 Fk-2 Fk-3 … F0]
这里的k=5;
根据以上地推关系,拼凑出矩阵A(转移矩阵) 为[1 0 1 1,1 0 0 0,0 1 0 0,0 0 1 0]
在计算矩阵的幂时,首先我们定义矩阵乘法的运算法则(矩阵乘法公式),然后对指数进行快速幂处理,通过减少乘法次数,化时间为对数量级。
ac代码:
#include <cstdio>
#include <iostream>
using namespace std;
const int MOD = 1000000007;
struct matrix
{
long long m[4][4];
}ans, base;
matrix multi(matrix a, matrix b)
{
matrix tmp;
for(int i = 0; i < 4; ++i)
{
for(int j = 0; j < 4; ++j)
{
tmp.m[i][j] = 0;
for(int k = 0; k < 4; ++k)
tmp.m[i][j] = (tmp.m[i][j] + ((a.m[i][k]%MOD) *(b.m[k][j]%MOD))%MOD) % MOD;
}
}
return tmp;
}
int fast_mod(int n) // 求矩阵 base 的 n 次幂
{
base.m={{1,0,1,1},{1,0,0,0},{0,1,0,0},{0,0,1,0}};
// ans 初始化为单位矩阵
ans.m={{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}};
while(n)
{
if(n & 1) //实现 ans *= t; 其中要先把 ans赋值给 tmp,然后用 ans = tmp * t
{
ans = multi(ans, base);
}
base = multi(base, base);
n >>= 1;
}
return ans.m[0][1];
}
int main()
{
int a,b,m;
while(scanf("%d%d%d", &a,&b,&m)!= -1)
{
if(m==1)
printf("%d\n",a);
if(m==2)
printf("%d\n",b);
if(m==3)
printf("%d\n",a+b);
if(m>3)
{
fast_mod(m-4);
long long sum=(((ans.m[0][0]%MOD)*4)%MOD+((ans.m[0][1]%MOD)*3)%MOD+((ans.m[0][2]%MOD)*2)%MOD+((ans.m[0][3]%MOD)*1)%MOD)%MOD;
printf("%lld\n",sum);
}
}
return 0;
}
求 f(n)f(n的值
多组数据。(数据组数 T≤100T≤100)
每组数据包含 33 个不超过 109109 的正整数,分别代表 f(1),f(2)f(1),f(2) 和 nn 的值。
输出 f(n)mod(109+7)f(n)mod(109+7)。每组输出末尾有换行符。
题意:
题目类似斐波那契数列,但比斐波那契数列多了一项。
思路:
首先回忆斐波那契数列的计算方法:
一、递归
直接按照递推公式递归运算,此方法有大量重复计算,导致n趋近不足100就无法在有限时间内计算出来,通常作为递归讲解的例子。
二、动态规划(dp)
为了避免重复计算,我们通常用一个数组记录已经计算的值,采用记忆化搜索的动态规划,就可以减少重复计算。
根据dp
= dp[n-1] + dp[n-2] 从第三项开始迭代即可。
此方法的时间复杂度为O(n),线性时间复杂度,考虑在1s内,无法计算大于1e8的数据范围。
三、矩阵快速幂
数列的递推公式为:f(1)=1,f(2)=2,f(n)=f(n-1)+f(n-2)(n>=3)
用矩阵表示为:
进一步,可以得出直接推导公式:
所以,对矩阵[1,1,1,0]进行n-2次幂运算,就可以计算出f(n)的值,此时由于幂运算可以使用快速幂运算,可以用矩阵快速幂来进行计算。
此时的时间复杂度为O(logN)
对比斐波那契数列,将其推广
由于本题中的递推关系非线性,即有sin项的约束,f(x+1)=f(x)+f(x−1)+sin(πx2)f(x+1)=f(x)+f(x−1)+sin(πx2)。我们展开两项,得到f(x)
= f(x-1) + f(x-3) + f(x-4),通过sin的周期性,消去了常数项。
得到了一个四阶线性递推公式。
类比斐波那契数列的方法,我们需要找一个转移矩阵,满足上述矩阵运算。
由于斐波那契数列是二阶递推数列,所以存在一个2*2的矩阵A,使得:
[Fn Fn-1] = [Fn-1 Fn-2] * A
所以以上的四阶递推公式也一定存在一个4*4的矩阵A,满足
[Fn Fn-1 Fn-2 … Fn-k+1] = A*[Fn-1 Fn-2 Fn-3 …
Fn-k]
= …
= An-k+1 *[Fk-1 Fk-2 Fk-3 … F0]
这里的k=5;
根据以上地推关系,拼凑出矩阵A(转移矩阵) 为[1 0 1 1,1 0 0 0,0 1 0 0,0 0 1 0]
在计算矩阵的幂时,首先我们定义矩阵乘法的运算法则(矩阵乘法公式),然后对指数进行快速幂处理,通过减少乘法次数,化时间为对数量级。
ac代码:
#include <cstdio>
#include <iostream>
using namespace std;
const int MOD = 1000000007;
struct matrix
{
long long m[4][4];
}ans, base;
matrix multi(matrix a, matrix b)
{
matrix tmp;
for(int i = 0; i < 4; ++i)
{
for(int j = 0; j < 4; ++j)
{
tmp.m[i][j] = 0;
for(int k = 0; k < 4; ++k)
tmp.m[i][j] = (tmp.m[i][j] + ((a.m[i][k]%MOD) *(b.m[k][j]%MOD))%MOD) % MOD;
}
}
return tmp;
}
int fast_mod(int n) // 求矩阵 base 的 n 次幂
{
base.m={{1,0,1,1},{1,0,0,0},{0,1,0,0},{0,0,1,0}};
// ans 初始化为单位矩阵
ans.m={{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}};
while(n)
{
if(n & 1) //实现 ans *= t; 其中要先把 ans赋值给 tmp,然后用 ans = tmp * t
{
ans = multi(ans, base);
}
base = multi(base, base);
n >>= 1;
}
return ans.m[0][1];
}
int main()
{
int a,b,m;
while(scanf("%d%d%d", &a,&b,&m)!= -1)
{
if(m==1)
printf("%d\n",a);
if(m==2)
printf("%d\n",b);
if(m==3)
printf("%d\n",a+b);
if(m>3)
{
fast_mod(m-4);
long long sum=(((ans.m[0][0]%MOD)*4)%MOD+((ans.m[0][1]%MOD)*3)%MOD+((ans.m[0][2]%MOD)*2)%MOD+((ans.m[0][3]%MOD)*1)%MOD)%MOD;
printf("%lld\n",sum);
}
}
return 0;
}
相关文章推荐
- 中国(北方)大学生程序设计训练赛(第一周)E. water problem
- 中国(北方)大学生程序设计训练赛(第一周)(Problem E: Water Problem-矩阵快速幂)
- 中国(北方)大学生程序设计训练赛(第一周)(Problem B: 埃蒙的时空航道-最小割转dp+贪心)
- 中国(北方)大学生程序设计训练赛(第二周)(Problem C: A Water Problem-dp)
- 中国(北方)大学生程序设计训练赛(第一周)(Problem F: 等差区间-线段树+等差数列平方和公式)
- 中国(北方)大学生程序设计训练赛(第一周)(Problem D: 数学题-二分+双指针)
- 中国(北方)大学生程序设计训练赛(第一周)
- 中国(北方)大学生程序设计训练赛(第一周)-F(线段树)
- 中国(北方)大学生程序设计训练赛(第一周) (D E)
- 中国(北方)大学生程序设计训练赛(第二周)(Problem A: Common Substrings-hash)
- 中国(北方)大学生程序设计训练赛(第二周)(Problem B: A Boring Game-乱搞)
- 中国(北方)大学生程序设计训练赛(第一周)-A(生成树计数)
- 矩阵快速幂-中国(北方)大学生程序设计训练赛(第一周)Water Problem
- 等差区间(写的很脑残)——中国(北方)大学生程序设计训练赛(第一周)F
- 中国(北方)大学生程序设计训练赛(第二周)(Problem G: Connected Components-并查集)
- 中国(北方)大学生程序设计训练赛(第一周)-D (二分)
- 中国(北方)大学生程序设计训练赛(第二周) (A B D G)
- 中国(北方)大学生程序设计训练赛(第三周)(Interesting sequence-找规律)
- 中国(北方)大学生程序设计训练赛(第三周)(List likes playing card-期望)
- fzu 2278 YYS [第八届福建省大学生程序设计竞赛 Problem G] [概率]