其他分享
首页 > 其他分享> > 凌乱的地下室

凌乱的地下室

作者:互联网

P2100 凌乱的地下室

思路一

注:由于本题数据过大,这里只讲解思路以及 $ 50 $ 分代码,高精度请自便。

不妨设当有 \(n\) 个方块时,可能的摆放数为 $f(n) $ 。

显然\(f(1)=1,f(2)=2\)。

当计算 \(f(3)\) 时,不妨进行模拟(用\(1,2,3..\)代表方块):

\[这是f(2)的情况 \begin{bmatrix} \notag 1&2\\ 2&1 \end{bmatrix} \\\notag 显然,计算f(3)时应从以上两种状况转移: \begin{bmatrix} \notag 1&2&3\\ 1&3&2\\ 2&1&3 \end{bmatrix} \\ \therefore f(3)=3\\ \]

不妨我们再模拟一下\(f(4)\):

\[\notag f(3)=3 ~~ \begin{bmatrix} \notag 1&2&3\\ 1&3&2\\ 2&1&3 \end{bmatrix} \\ 模拟f(4): \begin{bmatrix} \notag 1&2&3&4\\ 1&2&4&3\\ 1&3&2&4\\ 2&1&3&4\\ 2&1&4&3 \end{bmatrix} \\\therefore f(4)=5 \]

由此我们可以大概猜测:数列\(f(n)\)是以\(1,2\)为前两项的斐波那契数列。

其实到这里已经可以做题了,但作为一名优秀的数学学子,这种不完全归纳法是不一定正确的,所以我们不妨浅证明一下:

可以看到,当由 \(n-1\) 递推 \(n\) 时,只有最后一个数为 \(n-1\) 的情况才会产生两种情况,而其他都只会有一种。

\[\notag eg: \\ n=3 ~~~ \begin{bmatrix} \notag 1&2&3&(1)\\ 1&3&2&(2)\\ 2&1&3&(3) \end{bmatrix} \\ n=4时,只有(1)(3)行可以产生2个的情况而(2)仅会产生一种情况\\ 而接下来又会有4行的末尾为4,当递推n=5时就会有四行可以产生两种情况。 \]

而这个末尾为\(n\)的情况是如何产生的呢?

自然是在\(n-1\)的每一重情况后面都加上一个\(n\)就得到了。

由此可得:

\[\notag n>=2时,f(n)=f(n-1)+f(n-2) \]

再用数学归纳法证明即可。

CODE(50tps)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn = 3123123;
ll ll_maxn=1e18;
inline ll read_int(){
    ll a=0,f=0,g=getchar();
    while(g<'0'||g>'9'){if(g=='-') f=1;g=getchar();}
    while('0'<=g&&g<='9') a=a*10+g-'0',g=getchar();
    return f ? -a : a;
}

inline void write(ll s,bool f){
    ll top=0,a[40];
    if(s<0) s=-s,putchar('-');
    while(s) a[++top]=s%10,s/=10;
    if(top==0) a[++top]=0;
    while(top) putchar(a[top]+'0'),top--;
    if(f) putchar('\n');
}

const ll mod=1e8; 
const ll maxm = 5;
struct JZ{
    ll jz[maxm][maxm];
    ll m,n;
    inline void clear(){memset(jz,0,sizeof jz),n=0,m=0;}
    JZ operator * (JZ a) const{
        JZ b;
        b.clear();
        b.m=m,b.n=a.n;
        for(ll i=1;i<=b.m;i++){
            for(ll e=1;e<=b.n;e++){
                for(ll k=1;k<=n;k++){
                    b.jz[i][e]+=jz[i][k]*a.jz[k][e];
                    b.jz[i][e]%=mod;
                }
            }
        }
        return b;
    }
    inline void _write(){
        for(ll i=1;i<=m;i++){
            for(ll e=1;e<=n;e++){
                write(jz[i][e],0),putchar(' ');
            }
            puts("");
        }
    }
    inline void build(ll M,ll N){
        clear();
        m=M,n=N;
        for(ll i=1;i<=m;i++){
            for(ll e=1;e<=n;e++){
                jz[i][e]=read_int();
            }
        }
    }
    inline void bz(ll N){
        clear();
        n=m=N;
        for(ll i=1;i<=n;i++) jz[i][i]=1;
    }
};

inline void read(){
	JZ a1,b1;
	ll n=read_int()-1;
	a1.clear();
	a1.m=2,a1.n=1;
	a1.jz[1][1]=1,a1.jz[2][1]=2;
	b1.bz(2);
	JZ a2;
	a2.clear();
	a2.m=a2.n=2;
	a2.jz[1][1]=0,a2.jz[1][2]=1,a2.jz[2][1]=1,a2.jz[2][2]=1;
	while(n){
		if(n&1) b1=b1*a2;
		a2=a2*a2,n>>=1;
	}
	a1=b1*a1;
	write(a1.jz[1][1],1);
}

int main (){
	ll T=read_int();
	while(T--)	read();
}

思路二

既然知道了是斐波那契数列,为什么不直接用其通项公式计算呢?

CODE(100tps)

//题解 
#include<bits/stdc++.h>
using namespace std;
map<long long ,long long>mmp;     //map优化递归求斐波拉契
long long a,b,c,d;
long long dfs(long long a){
	if(a==1) return 1;
	if(a==2) return 1;
	if(mmp[a]!=0) return mmp[a];
	else return 
		mmp[a]=a%2==0?(dfs(a/2)+2*dfs(a/2-1))*dfs(a/2)%100000000 :
		 (dfs(a/2)*dfs(a/2)+dfs(a/2+1)*dfs(a/2+1))%100000000;
}        //通项公式求斐波拉契
inline long long read(){
	long long X=0;char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) X=(X*10+ch-'0')%150000000,ch=getchar();
	return X;
}     //取摸150000000直接在快速读入中进行
int main(){
	a=read();
	cout<<dfs(a+1);
}          //主程序真短

标签:ch,地下室,ll,dfs,notag,long,bmatrix,凌乱
来源: https://www.cnblogs.com/LQX-OI/p/16379801.html