2018年第三阶段个人训练赛第九场6760 Problem H 九连环

题目描述

九连环是一种源于中国的传统智力游戏。如图所示,九个圆环套在一把“剑”上,并且互相牵连。游戏的目标是把九个圆环从“剑”上卸下。

圆环的装卸需要遵守两个规则。
第一个(最右边)环任何时候都可以装上或卸下。
如果第k个环没有被卸下,且第k个环右边的所有环都被卸下,则第k+1个环(第k个环左边相邻的环)可以任意装上或卸下。
与魔方的千变万化不同,解九连环的最优策略是唯一的。为简单起见,我们以“四连环”为例,演示这一过程。这里用1表示环在“剑”上,0表示环已经卸下。
初始状态为1111,每部的操作如下:
1101(根据规则2,卸下第2个环)
1100(根据规则1,卸下第1个环)
0100(根据规则2,卸下第4个环)
0101(根据规则1,装上第1个环)
0111(根据规则2,装上第2个环)
0110(根据规则1,卸下第1个环)
0010(根据规则2,卸下第3个环)
0011(根据规则1,装上第1个环)
0001(根据规则2,卸下第2个环)
0000(根据规则1,卸下第1个环)
由此可见,卸下“四连环”至少需要10步。随着环数增加,需要的步数也会随之增多。例如卸下九连环,就至少需要341步。
请你计算,有n个环的情况下,按照规则,全部卸下至少需要多少步。

 

输入

输入第一行为一个整数m ,表示测试点数目。
接下来m行,每行一个整数n。

 

输出

输出共m行,对应每个测试点的计算结果。

样例输入

3
3
5
9

 

样例输出

5
21
341

 

提示

对于10%的数据,1≤n≤10。
对于30%的数据,1≤n≤30。
对于100%的数据,1≤n≤105,1≤m≤10。

思路:推公式,大数模板

f(n)=(-1)^(n-1)/6+2/3*2^n-1/2

即当n为奇数时f(n)=(2^(n+1)-1)/3

   当n为偶数时f(n)=(2^(n+2)-2)/3

#include<bits/stdc++.h>
using namespace std;
const int ten[4]= {1,10,100,1000};
const int max1=10000;
struct BigNumber
{
    int d[max1];
    BigNumber (string s)
    {
        int len=s.size();
        d[0]=(len-1)/4+1;
        int i,j,k;
        for(i=1; i<max1; i++)
            d[i]=0;
        for(i=len-1; i>=0; i--)
        {
            j=(len-i-1)/4+1;
            k=(len-i-1)%4;
            d[j]+=ten[k]*(s[i]-'0');
        }
        while(d[0]>1&&d[d[0]]==0)
            --d[0];

    }
    BigNumber(){
        *this=BigNumber(string("0"));
    }
    string toString(){
        string s("");
        int i,j,temp;
        for(i=3; i>=1; i--)
            if(d[d[0]]>=ten[i])
                break;
        temp=d[d[0]];
        for(int j=i; j>=0; j--){
            s=s+(char)(temp/ten[j]+'0');
            temp%=ten[j];
        }
        for(i=d[0]-1; i>0; i--){
            temp=d[i];
            for(int j=3; j>=0; j--){
                s=s+(char)(temp/ten[j]+'0');
                temp%=ten[j];
            }
        }
        return s;
    }
} zero("0"),one("1"),two("2"),three("3"),mid1[2000],d,temp;
BigNumber operator +(const BigNumber &a,const BigNumber &b)
{
    BigNumber c;
    c.d[0]=max(a.d[0],b.d[0]);
    int i,x=0;
    for(i=1; i<=c.d[0]; ++i){
        x=a.d[i]+b.d[i]+x;
        c.d[i]=x%10000;
        x/=10000;
    }
    while(x!=0){
        c.d[++c.d[0]]=x%10000;
        x/=10000;
    }
    return c;
}
BigNumber operator *(const BigNumber &a,const BigNumber &b)
{
    BigNumber c;
    c.d[0]=a.d[0]+b.d[0];
    int i,j,x;
    for(i=1; i<=a.d[0]; i++){
        x=0;
        for(j=1; j<=b.d[0]; j++){
            x=a.d[i]*b.d[j]+x+c.d[i+j-1];
            c.d[i+j-1]=x%10000;
            x/=10000;
        }
        c.d[i+b.d[0]]=x;
    }
    while( (c.d[0]>1) && (c.d[c.d[0]]==0) )
        --c.d[0];
    return c;
}
BigNumber operator - (const BigNumber &a,const BigNumber &b){
    BigNumber c;
    c.d[0]=a.d[0];
    int i,x=0;
    for(i=1;i<=c.d[0];i++){
        x=10000+a.d[i]-b.d[i]+x;
        c.d[i]=x%10000;
        x=x/10000-1;
    }
    while((c.d[0]>1)&&(c.d[c.d[0]]==0))--c.d[0];
    return c;
}
bool smaller(const BigNumber &a,const BigNumber &b,int delta){
    if(a.d[0]+delta!=b.d[0])return a.d[0]+delta<b.d[0];
    int i;
    for(i=a.d[0];i>0;i--)
        if(a.d[i]!=b.d[i+delta])
            return a.d[i]<b.d[i+delta];
    return true;
}
void Minus(BigNumber &a,const BigNumber &b,int delta){
    int i,x=0;
    for(int i=1;i<=a.d[0]-delta;i++){
        x=10000+a.d[i+delta]-b.d[i]+x;
        a.d[i+delta]=x%10000;
        x=x/10000-1;
    }
    while((a.d[0]>1)&&(a.d[a.d[0]]==0))a.d[0]--;
}
BigNumber operator /(const BigNumber &a,const BigNumber &b)
{

    BigNumber c;
    d=a;
    int i,j,temp;
    mid1[0]=b;
    for(int i=1; i<=13; i++){
        mid1[i]=mid1[i-1]*two;
    }
    for(i=a.d[0]-b.d[0]; i>=0; i--){
        temp=8192;
        for(int j=13; j>=0; j--){
            if(smaller(mid1[j],d,i)){
                Minus(d,mid1[j],i);
                c.d[i+1]+=temp;
            }
            temp/=2;
        }
    }
    c.d[0]=max(1,a.d[0]-b.d[0]+1);
    while((c.d[0]>1)&&(c.d[c.d[0]]==0))--c.d[0];
    return c;
}
BigNumber qpow(BigNumber a,int n){
    BigNumber ans=one;
    while(n){
        if(n&1){
            ans=ans*a;
        }
        a=a*a;
        n>>=1;
    }
    return ans;
}
BigNumber f1,f2,f3,d1,d2,d3;
void init()
{
    f1=one,f2=one+one,f3=one+one+one+one+one;
    d1=one,d2=one+one+one,d3=one+one+one+one+one+one+one;
}
int main(){
    init();
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        BigNumber ans=qpow(two,(n+1));
        if(n&1)
        {
            ans=ans-one;
            ans=ans/three;
        }
        else
        {
            ans=ans-two;
            ans=ans/three;
        }
        cout<<ans.toString()<<endl;

    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值