• 「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;
    }

     

  • 相关阅读:
    免密码远程登录和远程操作
    1、linux网络服务实验 用PuTTY连接Linux
    巧用CAS解决数据一致性问题
    第一天
    图像处理02
    图像处理01
    Poem 01(转)
    CS229 Lecture 01
    日本語1
    latex测试
  • 原文地址:https://www.cnblogs.com/ajmddzp/p/11832565.html
Copyright © 2020-2023  润新知