打表,找规律,爆搜(比赛经验)

有的时候,经验确实重要,放手一搏也很重要的,暴力出奇迹!

G. Getting in Shape

小清新构造题,给定一个数字(N(0leq Nleq10^{15})),要求构造一个字符串满足一下条件:
1.最后一个字符必须是B。且整个字符串只能有A,B构成。
2.这个字符串的方案数恰好为N,方案数这样定义:从第一个字符开始,顺序拿走字符,A后的字符可拿可不拿,B则无要求。
拿到这个题的第一反应就是去找A与B对方案数的影响。先考虑对于一个已知的字符串,如何求他的方案数。这里有一个比较重要的结论因为B并没有特殊,而A后面的字符可取可不取,则我们可以将整个字符串以B来间隔开,因为B之前的字符串和后面的字符串的方案数是直接相乘的关系。考虑(1)B(2),考虑这个字符串,(1)B有若干个取法,但它的取法无论怎么取,都影响不到(2)的取法,因为有B的间隔,导致(1)影响不到2(即使(1)最后一个字符是A)。所以总体的方案数就是(1)B的方案数与(2)的方案数相乘即可。考虑第一个规则,我们完全可以将整个字符串划分成若干个AAA...AB的形式,然后将他们的方案数相乘即可。那么接下来的问题就是考虑AAA..AB的方案数怎么求的问题?发现,这就是一个简单的递推,B只有一种,AB有两种,AAB有(2+1)种,最后的递推公式为斐波那契数列的递推。然后可以通过暴力打表,发现f[73]就大于(10^{15}),接下来我们的问题就是给定N后,判断是否有若干个f[i]的乘积等于N。至于这个直接爆搜!(暴力出奇迹!)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int num;
ll f[110],N;
vector<int>b;

inline void dfs(ll n,int m)
{
    if(n==1)//搜到答案。 
    {
        for(auto s:b)
        {
            for(int i=1;i<=s;++i) printf("A");
            printf("B");
        }
        exit(0);
    }
    for(int i=m;i>=1;--i)
    {
        if(f[i]<=n&&n%f[i]==0)
        {
            b.push_back(i);
            dfs(n/f[i],i);
            b.pop_back();
        }
    }
}

int main()
{
//    freopen("1.in","r",stdin);
    scanf("%lld",&N);
    f[1]=2;f[2]=3;
    for(int i=3;;++i)
    {
        f[i]=f[i-1]+f[i-2];
        if(f[i]>N) 
        {
            num=i;
            break;
        }
    }
    dfs(N,num);
    printf("IMPOSSIBLE");
    return 0;
} 
原文地址:https://www.cnblogs.com/gcfer/p/15516584.html