1. 程式人生 > 實用技巧 >試題 演算法提高 翔集合(矩陣快速冪)

試題 演算法提高 翔集合(矩陣快速冪)

問題描述   集合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 思路:先想出遞推式,當n>=4,考慮f[n]的翔集合數量。 當集合不包含最後一項n,有f[n-1]個; 當集合包含最後一項,1.當n-1,n-2不在集合中,n和前n-3滿足的翔集合均可構成翔集合,有f[n-3]個; 2.當每個翔集合只含2個數(含n),則有n-3個; 則推出f[n]=f[n-1]+f[n-3]+n-3; 觀察資料規模,不能直接dp遞推,選擇矩陣快速冪求解; 我們構成一個矩陣A1=[ f[n-1], f[n-2], f[n-3], n-3, 1 ]; 則有A2 =[ f[n], f[n-1], f[n-2], n-2, 1 ];
構造矩陣B使得A1*B=A2; 計算得 B=

則所以要得到第n項的翔集合數通過[ f[3] f[2] f[1] 1 1 ]*B^n-3 取一行一列即是所求f[n]。

B^n-3進行矩陣快速冪即可。

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
struct mat{
    ll m[6][6];
}unit;
ll n;
const ll mod=1e6+7;
mat mul(mat a1,mat a2){
    mat temp;
    for(int i=1;i<=5
;i++){ for(int j=1;j<=5;j++){ temp.m[i][j]=0; for(int k=1;k<=5;k++){ temp.m[i][j]=(temp.m[i][j]+(a1.m[i][k]*a2.m[k][j])%mod)%mod; } } } return temp; } ll quick_mat(ll n){ mat ans; memset(ans.m,0,sizeof(ans.m));
for(int i=1;i<=5;i++)ans.m[i][i]=1; while(n){ if(n&1)ans=mul(ans,unit); unit=mul(unit,unit); n>>=1; } mat tot; tot.m[1][1]=0,tot.m[1][2]=0,tot.m[1][3]=0,tot.m[1][4]=1,tot.m[1][5]=1; ll sum=0; for(int i=1;i<=5;i++){ sum+=tot.m[1][i]*ans.m[i][1]; sum%=mod; } return sum; } int main(){ cin>>n; if(n==1||n==2){cout<<"0"<<endl;return 0;} else if(n==3){cout<<"1"<<endl;return 0;} else{ unit.m[1][1]=1,unit.m[1][2]=1,unit.m[1][3]=0,unit.m[1][4]=0,unit.m[1][5]=0; unit.m[2][1]=0,unit.m[2][2]=0,unit.m[2][3]=1,unit.m[2][4]=0,unit.m[2][5]=0; unit.m[3][1]=1,unit.m[3][2]=0,unit.m[3][3]=0,unit.m[3][4]=0,unit.m[3][5]=0; unit.m[4][1]=1,unit.m[4][2]=0,unit.m[4][3]=0,unit.m[4][4]=1,unit.m[4][5]=0; unit.m[5][1]=0,unit.m[5][2]=0,unit.m[5][3]=0,unit.m[5][4]=1,unit.m[5][5]=1; cout<<quick_mat(n-3)<<endl; return 0; } }