HDU 6030 Happy Necklace
题目:http://acm.hdu.edu.cn/showproblem.php?pid=6030
题意:给出红蓝两种,然后排成一个字符串,要求在每一个长度为素数的区间里面是的r(red)的数量不小与b(blue)的数量;
解法:难点在于如何找规律。容易推知只要长度为2或3的字符串满足r>=b,那么之后的素数(5、7......)都会满足r>=b。
假设现在有一个n字符的串,其方案数为f(n),考虑一下能否从f(n-1)推得f(n)?
情况1.如果这个串最后一个字符是r,那么倒数第二个既可以是r也可以是b。所以此时f(n) = f(n-1).
情况2.如果这个串最后一个字符是b,那么倒数第二个一定是r才可以满足“长度为2且r>=b”的条件,那么倒数第三个就一定是r,否则不满足“长度为3且r>=b”的条件。所以此时f(n) = f(n-3)
综上,可得f(n) = f(n-1) + f(n-3).
由于输入量巨大(1018),所以用数组储存递推结果是不可行的,而每次递推又太慢,所以只能用矩阵快速幂来解决。
由于递推式中出现f(n-3),所以传递矩阵一定是3*3的。
经过推导可得传递矩阵为
1 1 0
0 0 1
1 0 0
AC:
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <cmath> #include <vector> using namespace std; typedef long long ll; const ll m = 1000000000+7; const int N = 4; struct mar { ll a[N][N]; };//结构化矩阵 mar mul(mar x,mar y) { mar temp; for(int i = 1 ; i < N ; i++) for(int j = 1 ; j < N ; j++) temp.a[i][j] = 0; for(int i = 1 ; i < N ; i++) for(int j = 1 ; j < N ; j++) for(int k = 1 ; k < N ; k++) temp.a[i][j] += (x.a[i][k]%m*y.a[k][j]%m)%m; return temp; }//矩阵乘法 mar quickpow(mar a,ll n) { mar res; res.a[1][1] = 1; res.a[1][2] = 0; res.a[1][3] = 0; res.a[2][1] = 0; res.a[2][2] = 1; res.a[2][3] = 0; res.a[3][1] = 0; res.a[3][2] = 0; res.a[3][3] = 1;//初始化为单位矩阵 while(n) { if(n&1) res = mul(res,a); a = mul(a,a); n>>=1; } return res; }//矩阵快速幂核心代码 int main() { int t; ll n; cin >> t; while(t--) { cin >> n; if(n==2) {cout << 3 <<endl;continue;} mar A; A.a[1][1] = 1; A.a[1][2] = 0; A.a[1][3] = 1; A.a[2][1] = 1; A.a[2][2] = 0; A.a[2][3] = 0; A.a[3][1] = 0; A.a[3][2] = 1; A.a[3][3] = 0;//构造传递矩阵 mar res = quickpow(A,n-2); cout << ((res.a[1][1] + res.a[1][2] + res.a[1][3]) % m+ (res.a[2][1] + res.a[2][2] + res.a[2][3]) % m+(res.a[3][1] + res.a[3][2] + res.a[3][3]) % m)%m <<endl; } return 0; }