51Nod 1167 计算递推

题意

F ( N ) = 1 ( 0 <= N <= K 1 )
F ( N ) = F ( N K ) + F ( N / K ) N % K == 0 )
F ( N ) = F ( N 1 ) ( N % K 0 )
给定 N K ,求 F ( N ) K [ 2 , 10 ] N K 50
时间限制:2秒。

解题思路

如果只看到了自变量由大到小,是很难想到怎么做的。
拿出笔来画一画,其实自变量增大的途径有2条,一是加1,一是乘以K。
答案就是 0 加到 N 的方案数。
也就是 N 能够拆分成 Σ K x 的方案数。
那么是不是和之前的一题很像啊?
另外一题的链接
这题的解题报告
这题的设法也是挺类似的,但是要注意一点。
显然 N = Σ i = 0 l o g K N a i K i
可以发现 a i 1 。所以转移的时候要借助一个辅助数组。
f [ i ] [ j ] 表示 K i 可以拆分成的最大的数为 K j 的方案数。
g [ j ] [ k ] 表示 j K i 可以拆分成最大的数为 K k 的方案数。

g [ j ] [ k ] = Σ l = 0 k g [ j 1 ] [ l ] f [ i 1 l ] [ k l ]

f [ i ] [ j ] = g [ K ] [ j ]
在计算答案的时候,设 h [ i ] [ j ] 表示做到 K 进制下的第 i 位,并且拆分的最大值为 K j 的方案数。
转移跟 f [ i ] [ j ] 的转移类似。

高精度卡常?

我AC了之后,看了某些大神的代码,发现了一些可以优化的地方。(当然优化之后还是不够他跑得快)
①在赋值的时候不需要将整个数组赋值,只需要更新位数和对应位数的值。
②高精度乘法
“%”运算常数大,所以要减少”%”运算的次数。
开一个 L L t 数组,每次 t [ i + j ] + = a [ i ] b [ j ] ,然后最后扫的时候再进位。

心得

N 要转化为 K 进制,才能够看清楚问题的本质。
②顺着推不行考虑倒着来。
③有一个小trick,就是 N = Σ i = 0 l o g K N a i K i a i 1 ,这个时候心态不要崩了,因为解决问题的大方向总是对的,这个时候可以考虑什么辅助的转移。
④高精度常数可能太大,这样会吃亏的。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 52
#define M 155
#define mo 100000000
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
struct note{
    int w,a[M];
    void operator = (const note b){
        int i;
        w=b.w;
        fo(i,1,w)a[i]=b.a[i];
    }
};note f[N][N],g[N][N],h[11][N],ans,n,c;
long long tt[260];
int i,j,k,K,l,ys,x,tot,wz;
int xx;
char s[32];
note operator + (const note &a,const note &b){
    c.w=a.w>b.w?a.w:b.w;
    memset(c.a,0,sizeof(c.a));
    int i;
    fo(i,1,c.w){
        c.a[i]+=i<=a.w?a.a[i]:0;
        c.a[i]+=i<=b.w?b.a[i]:0;
        c.a[i+1]+=c.a[i]/mo;
        c.a[i]%=mo;
    }
    if(c.a[c.w+1])c.w++;
    return c;
}
note operator * (const note &a,const note &b){
    int i,j;
    c.w=a.w+b.w-1;
    fo(i,0,c.w+1)tt[i]=0;
    long long kk;
    fo(i,1,a.w)fo(j,1,b.w){
        kk=1ll*a.a[i]*b.a[j];
        tt[i+j-1]+=kk;
    }
    fo(i,1,c.w){
        c.a[i]=tt[i]%mo;
        tt[i+1]+=tt[i]/mo;
        c.w=((tt[i]>=mo)&&(i==c.w))?c.w+1:c.w;
    }
    return c;
}
int main(){
    scanf("%d%s",&K,s+1);
    l=strlen(s+1);
    n.w=l/8+1;
    fo(i,1,l){
        int j=(l-i)/8;
        n.a[j+1]=n.a[j+1]*10+(s[i]-'0');
    }
    while(!n.a[n.w]&&n.w>1)n.w--;
    f[0][0].w=f[0][0].a[1]=1;
    fo(i,1,50){
        fo(j,0,i-1)g[1][j]=f[i-1][j];
        fo(j,2,K){
            fo(x,0,i-1){
                g[j][x].w=0;
                memset(g[j][x].a,0,sizeof(g[j][x].a));
                fo(k,0,x)g[j][x]=g[j][x]+g[j-1][k]*f[i-1-k][x-k];
            }
        }
        fo(j,0,i-1)f[i][j]=g[K][j];
        f[i][i].w=f[i][i].a[1]=1;
    }
    memset(g,0,sizeof(g));
    g[0][0].w=g[0][0].a[1]=1;
    wz=51;
    fo(i,0,50){
        ys=0;
        fd(j,n.w,1){
            x=n.a[j];
            n.a[j]=(1ll*ys*mo+x)/K;
            ys=(1ll*ys*mo+x)%K;
        }
        while(!n.a[n.w]&&n.w>1)n.w--;
        fo(j,0,i)h[0][j]=g[i][j];
        fo(j,1,ys)
            fo(x,0,i){
                h[j][x].w=0;
                memset(h[j][x].a,0,sizeof(h[j][x].a));
                fo(k,0,x)h[j][x]=h[j][x]+h[j-1][k]*f[i-k][x-k];
            }
        fo(j,0,i)g[i+1][j]=h[ys][j];
        if(n.w==1&&n.a[1]==0){
            n.w=0;
            wz=i+1;
            break;
        }
    }
    fo(i,0,50)ans=ans+g[wz][i];
    printf("%d",ans.a[ans.w]);
    fd(i,ans.w-1,1){
        xx=log(ans.a[i])/log(10)+1;
        fo(j,1,8-xx)putchar('0');
        printf("%d",ans.a[i]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/huangjingyuan107/article/details/80498111