试题 算法提高 翔集合(矩阵快速幂)

问题描述
  集合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;
    }
}
原文地址:https://www.cnblogs.com/mohari/p/13532219.html