围圈圈

Description

jzj都成年两个多月了,为了重拾自己的童心,jzj跑到幼儿园去当小老师啦~

幼儿园里一共有无数个不同性别不同高矮的小盆友,现在jzj要带着小朋友们做游戏,首先要把小盆友们排成一个圈,一轮游戏只能有N个小盆友参加。每个小盆友的性别是不同的,高矮也是不一样的(即每个小盆友的信息可以用两个参数来衡量,一种是男女,一种是高矮,一个小盆友只能或男或女,或高或矮……),当两个小盆友性别不一样高矮也不一样的时候,他们是不愿意手拉手排在相邻的位置的。现在jzj想知道一共有多少种方法把小盆友围成一个大小为N的圈圈。(考虑绝对位置,也就是如果这个圈圈逆时针或者顺时针转一个单位也被认为是不同的排法。)

p.s由于不同性别不同高矮的小盆友有无数个,所以排位置的时候你可以决定每个位置小盆友的性别和高矮。

Analysis

递推

先不管数据量,分析一下这道题的状态转移方法。对于n个小盆友围在一起,将其看作一条队列,针对对头和队尾的不同情况进行分析。

  1. 对头和队尾完全相同,可以由(n-1,1)与(n-1,2)进行推演
  2. 对头和队尾有一个元素相同,也可以由(n-1,1)与(n-1,2)推演,但是对于n-1个小盆友队头队尾无法匹配时,可以再来一个小盆友作为媒介进行连接

所以就有了第三种

  1. 对头和队尾无法匹配,可以由(n-1,2)和(n-1,3)推演而来。
dp[i][1]=dp[i-1][1]+dp[i-1][2]

dp[i][2]=2*dp[i-1][1]+dp[i-1][2]+2*dp[i-1][3]

dp[i][3]=dp[i-1][2]+dp[i-1][3]

复杂度O(n)

找规律

显然,对于n<=10^ 100,递推是没有指望的。打出来1~20的数据后发现,n为奇数时方案数为n^ 3 +1,偶数时为n^ 3 +4,所以打一个大整数快速幂就过了。

复杂度O(logn)

Code

#include <bits/stdc++.h>
#define mo 20101031

struct bigint{
    int len,num[510];
    bigint operator = (bigint eq){
        len=eq.len;
        memset(num,0,sizeof(num));
        for(int i=0;i<len;i++)
            num[i]=eq.num[i];
        return *this;
    }
    bigint operator = (std::string eq){
        len=eq.size();
        memset(num,0,sizeof(num));
        for(int i=0;i<len;i++)
            num[i]=eq[len-i-1]-'0';
        return *this;
    }
    bool parity(){
        return num[0]&1;
    }
    bigint operator / (int dv){
        bigint ans;
        ans.len=len;
        memset(ans.num,0,sizeof(ans.num));
        int bc=0;
        for(int i=len-1;i>=0;i--){
            bc=bc*10+num[i];
            if(bc>=dv)ans.num[i]=bc/dv,bc%=dv;
        }
        while(ans.len-1&&!ans.num[ans.len-1])ans.len--;
        return ans;
    }
    bigint operator /= (int dv){
        return *this=*this/dv;
    }
    friend std::ostream& operator << (std::ostream& out,bigint ans){
        for(int i=ans.len-1;i+1;i--)
            out<<ans.num[i];
        return out;
    }
};

long long ans,mt;
bigint n;

int main(){
    freopen("circle.in","r",stdin);
    freopen("circle.out","w",stdout);
    std::string get;
    std::cin>>get;
    n=get;
    ans=1;
    mt=3;
    int add=n.parity()?1:3;
    while(n.len>1||n.num[0]>0){
        if(n.parity())ans=ans*mt%mo;
        n/=2;
        mt=mt*mt%mo;
    }
    std::cout<<(ans+add)%mo<<std::endl;
    return 0;
}

矩阵加速

当然找规律也不是万能的,所以可以利用我们得出的递推式进行矩阵加速。

要掌握找递推式再加速这一思维体系。

Code O(9·logn)

##include <bits/stdc++.h>
const int N=11,Mod=20101031;
typedef long long int64;
struct bigint{
    int l,s[110];
    void clear(){
        l=0;
        memset(s,0,sizeof(s));
    }
    void minus(){
        for(int i=0;i<l;i++){
            s[i]--;
            if(s[i]<0)s[i]+=10;
            else break;
        }
    }
    bool exist(){
        return (l-1)||(s[0]);
    }
    bool parity(){
        return s[0]&1;
    }
    bigint operator = (std::string x){
        clear();
        l=x.size();
        for(int i=0;i<l;i++)
            s[i]=x[l-i-1]-'0';
        return *this;
    }
    bigint operator / (int x){
        bigint y;
        y.clear();
        y.l=l;
        int bc=0;
        for(int i=l-1;i>=0;i--){
            bc=bc*10+s[i];
            if(bc>=x)y.s[i]=bc/x,bc%=x;
        }
        while(y.l-1&&!y.s[y.l-1])y.l--;
        return y;
    }
    bigint operator /= (int x){
        return *this=*this/x;
    }
    friend std::ostream& operator << (std::ostream& out,bigint ans){
        for(int i=ans.l-1;i+1;i--)
            out<<ans.s[i];
        return out;
    }
};
struct Matrix{
    int h,l;
    int64 s[N][N];
    void clear(){
        h=l=0;
        memset(s,0,sizeof(s));
    }
    Matrix operator + (Matrix x){
        Matrix y;
        y.clear();
        y.h=h,y.l=l;
        for(int i=0;i<h;i++)
            for(int j=0;j<l;j++)
                y.s[i][j]=(s[i][j]+x.s[i][j])%Mod;
        return y;
    }
    Matrix operator * (Matrix x){
        Matrix y;
        y.clear();
        y.h=h,y.l=x.l;
        for(int i=0;i<h;i++)
            for(int j=0;j<x.l;j++)
                for(int k=0;k<l;k++)
                    y.s[i][j]=(y.s[i][j]+s[i][k]*x.s[k][j])%Mod;
        return y;
    }
}ans,cal,E;
void init(){
    ans.clear();
    ans.h=1,ans.l=3;
    ans.s[0][0]=4,ans.s[0][1]=0,ans.s[0][2]=0;
    cal.clear();
    cal.h=cal.l=3;
    cal.s[0][0]=cal.s[1][0]=cal.s[1][1]=cal.s[1][2]=cal.s[2][2]=1;
    cal.s[0][1]=cal.s[2][1]=2;
    E.clear();
    E.h=E.l=3;
    for(int i=0;i<3;i++)
        E.s[i][i]=1;
}
Matrix fast_pow(bigint k){
    Matrix a,b;
    a=cal,b=E;
    while(k.exist()){
        if(k.parity())b=b*a;
        k/=2;
        a=a*a;
    }
    return b;
}
int main(){
    //freopen("circle.in","r",stdin);
    //freopen("circle.out","w",stdout);
    bigint k;
    std::string get;
    std::cin>>get;
    k=get;
    k.minus();
    init();
    ans=ans*fast_pow(k);
    printf("%lld\n",(ans.s[0][0]+ans.s[0][1])%Mod);
    return 0;
}

转载于:https://www.cnblogs.com/qswx/p/9494982.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值