九连环

九连环

题解

一看到这题目,就知道不是什么好题。

好吧,一眼就感觉是个dp。根据样例解释我们很容易发现:

dp_{i}为i连环所需的最小步数,则

dp_{i}= dp_{i-1}+2dp_{i-2}+1

哇,这么简单。忽然发现数据范围为1e5,要炸!!!

高精明显会TLE,所以我们来想一想如何用其它方法做。

巨佬可忽略下面这段。

我们把每个的值列出来:

1,2,5,10,21,...

完全没有规律,咋办?不你看错了,有规律的,换成2进制。

1,10,101,1010,10101,...

发现了规律,1与0间断出现。接下来我们把相邻两个相加:

1,11,111,1111,11111,...

所以f_{i}+f_{i+1}=2^{i+1},因此f_{i}=\left \lfloor \frac{2^{i+1}}{3} \right \rfloor,然后,就可以用FFT快速幂快速算出了。

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#define MAXN 150000
using namespace std;
typedef long long LL;
//#define int LL
const int W=10;
const double PI=acos(-1);
#define gc() getchar()
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=gc();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=gc();}
	while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=gc();}
	x*=f;
}
int rev[MAXN];
struct complex{
    double x,y;
    complex(){x=y=0;}
    complex(double _x,double _y):x(_x),y(_y){}        
	complex operator + (complex b){return complex(x+b.x,y+b.y);}
    complex operator - (complex b){return complex(x-b.x,y-b.y);}
    complex operator * (complex b){return complex(x*b.x-y*b.y,x*b.y+y*b.x);}    
};
void FFT(int lim,complex *a,int typ){
	for(int i=0;i<lim;i++)if(i<rev[i])swap(a[i],a[rev[i]]);
	for(int i=1;i<lim;i<<=1){
		complex T(cos(PI/i),typ*sin(PI/i));
		for(int W=i<<1,j=0;j<lim;j+=W){
            complex n(1,0);
            for(int k=0;k<i;++k,n=n*T){
                complex x(a[j+k]),y(n*a[i+j+k]);
                a[j+k]=x+y;a[i+j+k]=x-y;
            }
        }
	}
}
struct bignum{
	int num[MAXN],len;
	bignum(){
		memset(num,0,sizeof(num));len=1;
	}
	bignum(int x){
		memset(num,0,sizeof(num));len=0;
		if(!x){len=1;return;}
		while(x)num[len++]=x%W,x/=W; 
	}
	void print(){
		for(int i=len-1;i>=0;i--)
			printf("%d",num[i]);
		puts("");
	}
	void operator /= (int x){
		int ys=0,lenn=0;
		for(int i=len-1;i>=0;i--){
			ys=ys*W+num[i];
			if(ys<x)num[i]=0;
			else{
				if(!lenn)lenn=i+1;
				num[i]=ys/x;ys%=x;
			}
		} 
		len=max(lenn,1);
	}
	void operator *= (const bignum b){
		static complex A[MAXN],B[MAXN];
		int lenn=len+b.len-1,lim=1,L=0;while(lim<lenn)lim<<=1,L++;
		rev[0]=0;for(int i=1;i<=lim;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<L-1);
		for(int i=0;i<lim;i++){
			A[i]=complex(i<len?num[i]:0,0);
			B[i]=complex(i<b.len?b.num[i]:0,0);
		}
		FFT(lim,A,1);FFT(lim,B,1);
		for(int i=0;i<lim;i++)A[i]=A[i]*B[i];FFT(lim,A,-1);
		for(int i=0;i<lenn;i++)num[i]=(int)(A[i].x/lim+0.5);
		len=lenn;num[len]=0;
		for(int i=0;i<len;i++)num[i+1]+=num[i]/W,num[i]%=W;
		if(num[len])len++;
	}
}res,a;
int m,n;
signed main(){
	read(m);
	while(m--){
		read(n);n++;
		res=bignum(1),a=bignum(2);
		while(n){
			if(n&1)res*=a;
			a*=a;n>>=1;
		}
		res/=3;
		res.print();
	}
    return 0;
}

谢谢!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值