C语言实现 蓝桥杯 算法提高 翔集合

试题 算法提高 翔集合

                                                                                  蓝桥杯试题解答汇总链接

资源限制

       时间限制:1.0s 内存限制:8.0MB


问题描述

       集合M至少有两个元素(实数),且M中任意两个元素差的绝对值都大于2,则称M为“翔集合”,已知集合S={1,2…,n},请求出n的子集中共有多少个翔集合。


输入格式

       输入共一行,一个整数n.(n>=2)


输出格式

       输出共一行,一个整数表示S的子集中共有多少个翔集合,由于个数可能过大,请输出这个值除以1000007的余数。


样例输入

4

样例输出

1

数据规模与约定

对于20%的数据,2<=n<=1000000
对于100%的数据,2<=n<=10^15

试题解析

设f(n)为集合Sn={1,2,3,4,…,n}的翔集合的个数
分析题目可以发现找不到由特殊去情况解决一般情况的方法,那么就的从一般情况去求特殊情况。现在要求的是f(n)分析:已知f(n-1)为不包含n的所有翔集合的个数、那么我们现在只需要求出包含n的翔集合的个数两个相加就能得到f(n)和f(n-1)的关系式
不包含n的情况:f(n-1)
包含n的情况:要和n组成翔集合首先排除n-1和n-2这两个数,[1,n-3]只能从这里里面选取若干个组成翔集合注意到f(n-3)为Sn-3的翔集合个数它的每一个翔集合都没有n-1和n-2两个数也就式说把n加到这写集合中它们任然是翔集合,所以包含n的且个数超过2个的翔集合的个数就是f(n-3)现在只剩下2个元素的集合易得为n-3个
综合①②可得:③f(n)=f(n-1)+f(n-3)+n-3 (f(3)=f(2)=0,f(4)=1)
所以现在就有两个做法一个是dp推下去或者是建立转移矩阵快速幂
但内存限制8MB且n最大为1e15所以dp既是滚动也可能会超时,所以我选择了转移矩阵麻烦点就麻烦点吧!观察递推式右边有三个变量f(n-1)、f(n-3)、n-3但f(n-1)和f(n-3)不连续所以需要补个
f(n-2)更加方便n-3转移需要一个辅助所以需要建立一个1*5的矩阵An=
|f(n-1) f(n-2) f(n-3) n-3 1|需要转移矩阵B使
An×B=|f(n) f(n-1) f(n-2) n-2 1|=An+1
那么An=A4Bn-4又A4=|f(4) f(3) f(2) 1 1|=|1 0 0 1 1|现在求Bn-4即可对矩阵B快速幂即可易得矩阵B(结合③)
C语言实现 蓝桥杯 算法提高 翔集合_第1张图片
这题可以和蓝桥杯的这个题目结合 斐波那契 起来看!
下面的sq和qpow可以说是一个模板这种类型的做的多了就会了,其实最难的还是关系式的推导。


代码

#include 
#define MOD 1000007
typedef long long ll;
typedef struct ma {
	ll m[5][5];
}mat;// 用结构体代替数组方便写函数 
mat E = {0}, B = {0};// E为单位矩阵、B为转移矩阵 
mat sq(mat a, mat b) {// 求矩阵的平方 
	mat c;
	int i, j, k;
	for(i = 0;i < 5; ++i) {
		for(j = 0;j < 5; ++j) {
			c.m[i][j] = 0;
			for(k = 0;k < 5; ++k) {
				c.m[i][j] = (c.m[i][j]+a.m[i][k]*b.m[k][j])%MOD;
			}
		}
	}
	return c;
}
mat qpow(mat a, ll n) {// 矩阵的快速幂
	mat temp = E; 
	for(;n;n>>=1,a = sq(a, a)) {
		if(n&1) {// 若为奇数则需要多乘一次 
			temp = sq(temp, a);
		}
	}
	return temp;// 返回矩阵a的n次方即矩阵temp 
}
int main() {
	ll n;
	scanf("%lld",&n);
	if(n < 4){
		printf("0");
		return 0;
	}
	B.m[0][0] = B.m[0][1] = B.m[1][2] = B.m[2][0] = B.m[3][0] = B.m[3][3] = B.m[4][3] = B.m[4][4] = 1;
    int i, j;
	for(i = 0;i < 5; ++i) {
		E.m[i][i] = 1;
	}
	mat ans = qpow(B, n-4);
	ll res = (ans.m[0][0]+2*ans.m[3][0]+ans.m[4][0])%MOD;// 矩阵A={1,0,0,2,1}和res的第一列相乘在取余即可 
	printf("%lld", res);
	return 0;
}

你可能感兴趣的:(蓝桥杯)