Forever Young

洛谷 P4910 帕秋莉的手环

题意

多组数据,给出一个环,要求不能有连续的\(1\),求出满足条件的方案数

\(1\le T \le 10, 1\le n \le 10^{18}\)

思路

20pts

暴力枚举(不会写

60pts

假设金珠子为\(0\),木珠子为\(1\),则不能有连续的木珠子

线性递推\(DP\),设\(f[i][0/1]\)表示当前填到第\(i\)位,第\(i\)位为金珠子/木珠子的方案数,那么有:

\[f[i][0] = f[i - 1][0] + f[i - 1][1] \]

\[f[i][1] = f[i-1][0] \]

但是要分成两种情况讨论

  • 第一个位置是\(0\),则\(f[1][0]=1,f[1][1]=0\),那么最后一个位置可以是\(0\)也可以是\(1\)

    所以此时对答案的贡献为\(f[n][0]+f[n][1]\)

  • 第一个位置是\(1\),则\(f[1][1]=1,f[1][0]=0\),那么最后一个位置只能是\(0\)

    所以此时对答案的贡献为\(f[n][0]\)

时间复杂度\(O(Tn)\),期望得分\(60\)

不知道为什么,也许是我写假了,只有48分

100pts

考虑用矩阵优化,目前的状态为\([f_{i,0},f_{i,1}]\),目标状态为\([f_{i+1,0},f_{i+1,1}]\),比较容易推出转移矩阵为

\[[f_{i,0},f_{i,1}] * \left[ \begin{matrix} 1 & 1 \\ 1 & 0 \end{matrix} \right] = [f_{i+1,0},f_{i+1,1}] \]

按照\(60\)分做法写矩阵快速幂就好了

代码

60pts

/*
Author:loceaner
假设不能有连续的1 
用f[i][0/1]表示选到了i处,第i处为白/黑的方案数
f[i][1] = f[i - 1][0]
f[i][0] = f[i - 1][1] + f[i - 1][0]
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int A = 1e6 + 10000;
const int B = 1e6 + 11;
const int mod = 1000000007;
const int inf = 0x3f3f3f3f;

inline int read() {
	char c = getchar(); int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
	return x * f;
}

int n, f[A][2];

int main() {
	int T = read();
	while(T--) {
		n = read();
		long long ans = 0;
		f[1][0] = 1, f[1][1] = 0;
		for (int i = 2; i <= n; i++) {
			f[i][1] = f[i - 1][0] % mod;
			f[i][0] = (f[i - 1][1] + f[i - 1][0]) % mod;
		} 
		ans = (f[n][0] + f[n][1]) % mod; 
		f[1][0] = 0, f[1][1] = 1;
		for (int i = 2; i <= n; i++) {
			f[i][1] = f[i - 1][0] % mod;
			f[i][0] = (f[i - 1][1] + f[i - 1][0]) % mod;
		} 
		(ans += f[n][0]) %= mod;
		cout << ans << '\n';
	}
	return 0;
}

100pts

/*
Author:loceaner
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;

const int A = 1e6 + 10000;
const int B = 1e6 + 11;
const int mod = 1000000007;
const int inf = 0x3f3f3f3f;

inline int read() {
	char c = getchar(); int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
	return x * f;
}

int n;

struct mat { int a[2][2]; } a, b, c;

mat operator * (const mat &a, const mat &b) {
	mat c;
	memset(c.a, 0, sizeof(c.a));
	for (int k = 0; k <= 1; k++) 
		for (int i = 0; i <= 1; i++)
			for (int j = 0; j <= 1; j++)
				c.a[i][j] = (c.a[i][j] + a.a[i][k] % mod * b.a[k][j] % mod) % mod;
	return c;
}

mat ksm(mat a, int b) {
	mat ans;
	memset(ans.a, 0, sizeof(ans.a));
	for (int i = 0; i <= 1; i++) ans.a[i][i] = 1;
	while (b) {
		if (b & 1) ans = ans * a;
		a = a * a, b >>= 1;
	}
	return ans;
}

signed main() {
	int T = read();
	while(T--) {
		n = read();
		int ans = 0;
		memset(a.a, 0, sizeof(a.a));
		a.a[0][0] = a.a[0][1] = a.a[1][0] = 1;
		a = ksm(a, n - 1);
		memset(b.a, 0, sizeof(b.a));
		b.a[0][0] = 1, b = b * a;
		ans = (ans + b.a[0][0] % mod + b.a[0][1]) % mod;
		memset(c.a, 0, sizeof(c.a));
		c.a[0][1] = 1, c = c * a;
		ans = (ans + c.a[0][0]) % mod;
		cout << ans << '\n';
	}
}
posted @ 2020-06-18 06:38  Loceaner  阅读(150)  评论(2编辑  收藏  举报