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(結合③)
在這裏插入圖片描述
這題可以和藍橋杯的這個題目結合 斐波那契 起來看!
下面的sq和qpow可以說是一個模板這種類型的做的多了就會了,其實最難的還是關係式的推導。


代碼

#include <stdio.h>
#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章