「NOIP模拟赛」数位和乘积(dp,高精)

 

 

 

统计方案数,要么组合数,要么递推(dp)了。

这是有模拟赛历史以来爆炸最狠的一次

  • T1写了正解,也想到开long long,但是开错了地方然后数组开大了结果100->0
  • T3看错题本来简单模拟又给我搞成0分
  • T5差分约束本来很简单但是又被我胡搞炸掉了.....

本题T4,难到爆炸的T2把我困住了.....

先讲讲考试看道题的想法:

思考了一会吗,推出几个结论,然后准备写了,感觉可以短时间A掉,结果被T2困住,一小时只优化掉了一个没啥用的n..(n^5logn的复杂度用爱过题)

然后现在来讲讲正解(也是时候背高精板子了)

首先,很容易想到,对于有0出现的数字,其结果一定是0,从而得出一个结论,对于0的结果就是10^n-9^n(不要让我证明)

然后进一步想到,对于给定k进行质因数分解,然后组合&&排列算方案数(我就是这里走了与dp不同的路)

但是发现这样并不好处理,进一步发现,如果k的质因数有大于10的,那么直接输出0(因为一个数位没法装下两个数)

然后,我在这里就暴毙了。

接下来就是正解了。

有了以上结论,我们能非常不容易地想到dp方程式:

$f[i][j][k][m][l]$表示在前i位中,2,3,5,7分别用了几次;

这个转移是真的令人折服。

for (int i = 1; i <= n;i++)
    {
        for (int j = a2; j >= 0;j--)
        {
            for (int k = a3; k >= 0;k--)
            {
                for (int l = a5; l >= 0;l--)
                {
                    for (int m = a7; m >= 0;m--)
                    {
                        if(j>=1)
                            f[j][k][l][m] = f[j - 1][k][l][m] + f[j][k][l][m];//2
                        if(k>=1)
                            f[j][k][l][m] = f[j][k - 1][l][m] + f[j][k][l][m];//3
                        if(j>=2)
                            f[j][k][l][m] = f[j - 2][k][l][m] + f[j][k][l][m];//4
                        if(l>=1)
                            f[j][k][l][m] = f[j][k][l - 1][m] + f[j][k][l][m];//5
                        if(j&&k)
                            f[j][k][l][m] = f[j - 1][k - 1][l][m] + f[j][k][l][m];//6
                        if(m>=1)
                            f[j][k][l][m] = f[j][k][l][m - 1] + f[j][k][l][m];//7
                        if(j>=3)
                            f[j][k][l][m] = f[j - 3][k][l][m] + f[j][k][l][m];//8
                        if(k>=2)
                            f[j][k][l][m] = f[j][k - 2][l][m] + f[j][k][l][m];//9
                    }
                }
            }
        }
    }
View Code

对每一位数进行分解,从而得出的方程,其实也不难想,只是没接触过所以想不到

于是,就可以开心地dp了吗?

不,你想死。

当n<=6的时候,方案数就已经上10万了,那么五十....

嘶~~

没有模数??

只能写高精了。

$$但!是!$$

高精*5维数组,会炸空间的....

所以,还要像01背包那样滚掉一维,所以就出现了上述的4维方程式。

于是,代码(这个高精的缺点就在:它太长了!!!!):

 

#include<bits/stdc++.h>
using namespace std;
const int maxn=30;
#define re register
#define max(x,y) ((x)>(y)?(x):(y))
struct node
{
    int len,s[110];
    node(){memset(s,0,sizeof(s)); len=1;}
    node(int val) {*this=val;}
    node(const char *val) {*this=val;}
    node operator = (const int &val)
    {
        re char s[810];
        sprintf(s,"%d",val);
        *this=s;return *this;
    }
    node operator = (const char *val)
    {
        len=strlen(val);
        while(len>1&&val[0]=='0') ++val,len--;
        for(re int i=0;i<len;++i) s[i]=val[len-i-1]-'0';
        return *this;
    }
    inline void deal()
    {
        while(len>1&&!s[len-1]) len--;
    }
    node operator + (const node &a) const 
    {
        node res;res.len=0;
        re int top=max(len,a.len),add=0;
        for(re int i=0;add||i<top;++i)
        {
            re int now=add;
            if(i<len) now+=s[i];
            if(i<a.len) now+=a.s[i];
            res.s[res.len++]=now%10;
            add=now/10;
        }
        return res;
    }
    node operator - (const node &a) const 
    {
        node res; res.len=0;re int del=0;
        for(re int i=0;i<len;++i){
            re int now=s[i]-del;
            if(i<a.len) now-=a.s[i];
            if(now>=0) del=0;
            else del=1,now+=10;
            res.s[res.len++]=now;
        }
        res.deal(); return res;
    }
    node operator * (const node &a) const 
    {
        node res; res.len=len+a.len;
        for(re int i=0;i<len;++i)
            for(re int j=0;j<a.len;++j)
                res.s[i+j]+=s[i]*a.s[j];
        for(re int i=0;i<res.len;++i)
            res.s[i+1]+=res.s[i]/10,res.s[i]%=10;
        res.deal(); return res;
    }
    node operator / (const node &a) const 
    {
        node res,cur=0;res.len=len;
        for(re int i=len-1;~i;--i){
            cur=cur*10,cur.s[0]=s[i];
            while(cur>=a)
                cur-=a,res.s[i]++;
        }
        res.deal(); return res;
    }
    node operator % (const node &a) const 
    {
        node res=*this/a;
        return *this-res*a;
    }
    node operator += (const node &a) {*this=*this+a; return *this;}
    node operator -= (const node &a) {*this=*this-a; return *this;}
    node operator *= (const node &a) {*this=*this*a; return *this;}
    node operator %= (const node &a) {*this=*this%a; return *this;}
    bool operator < (const node &a) const 
    {
        if(len!=a.len) return len<a.len;
        for(re int i=len-1;~i;i--) 
        if(s[i]!=a.s[i]) return s[i]<a.s[i];
        return false;
    }
    bool operator >  (const node &a) const {return a<*this;}
    bool operator <= (const node &a) const {return !(*this>a);}
    bool operator >= (const node &a) const {return !(*this<a);}
    bool operator == (const node &a) const {return !(*this>a||*this<a);}
    bool operator != (const node &a) const {return *this>a||*this<a;}
};

inline void print(const node &a)
{
    for(re int i=a.len-1;~i;--i) 
        printf("%d",a.s[i]); puts("");
}
int n,k,m;
node f[32][20][20][12];
int a2,a3,a5,a7;
void solve()
{
    node a,b;
    a.s[0]=1;
    b.s[0]=1;
    for(int i=1;i<=n;i++)
    {
        a*=9;
        b*=10;
    }
    b-=a;
    print(b);
}
int main()
{
    scanf("%d%d", &n, &k);
    if(k==0)
    {
        solve();
        return 0;
    }
    int t = k;
    while (k % 2 == 0)
        a2++,k /= 2;
    while (k % 3 == 0)
        a3++, k /= 3;
    while (k % 5 == 0)
        a5++, k /= 5;
    while (k % 7 == 0)
        a7++, k /= 7;
    if(k!=1)
    {
        printf("0");
        return 0;
    }
    f[0][0][0][0].s[0] = 1;
    f[0][0][0][0].len = 1;
    for (int i = 1; i <= n;i++)
    {
        for (int j = a2; j >= 0;j--)
        {
            for (int k = a3; k >= 0;k--)
            {
                for (int l = a5; l >= 0;l--)
                {
                    for (int m = a7; m >= 0;m--)
                    {
                        if(j>=1)
                            f[j][k][l][m] = f[j - 1][k][l][m] + f[j][k][l][m];//2
                        if(k>=1)
                            f[j][k][l][m] = f[j][k - 1][l][m] + f[j][k][l][m];//3
                        if(j>=2)
                            f[j][k][l][m] = f[j - 2][k][l][m] + f[j][k][l][m];//4
                        if(l>=1)
                            f[j][k][l][m] = f[j][k][l - 1][m] + f[j][k][l][m];//5
                        if(j&&k)
                            f[j][k][l][m] = f[j - 1][k - 1][l][m] + f[j][k][l][m];//6
                        if(m>=1)
                            f[j][k][l][m] = f[j][k][l][m - 1] + f[j][k][l][m];//7
                        if(j>=3)
                            f[j][k][l][m] = f[j - 3][k][l][m] + f[j][k][l][m];//8
                        if(k>=2)
                            f[j][k][l][m] = f[j][k - 2][l][m] + f[j][k][l][m];//9
                    }
                }
            }
        }
    }
    print(f[a2][a3][a5][a7]);
    return 0;
}

 

 

 

 

 

 

posted @ 2019-11-11 08:32  阿基米德的澡盆  阅读(207)  评论(0编辑  收藏  举报