ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

3362. 【NOI2013模拟】数数

2022-07-14 20:00:39  阅读:120  来源: 互联网

标签:数数 背包 return ll cnt 3362 NOI2013 sim 神犇


Description

神犇最近闲来无事,于是就思考哲学,研究数字之美。在神犇看来,如果一个数的各位能够被分成两个集合,而且这两个集合里的数的和相等,那么这个数就是优美的(具体原因就只有神犇才知道了)。现在神犇在思考另一个问题,在区间[A,B]中有多少个数是优美的?这个问题对于神犇来说很简单,相信对于你来说也不难。

Solution

先将目的转化成求 \(1\sim b\) 和 \(1\sim a-1\) 中的帅气的数字个数。然后二者相减。下面以求出 \(1\sim n\) 中的答案来讲解。

注意到一个数是否帅气只跟 0~9 各出现了多少次有关。考虑直接枚举,枚举完后用背包来判断是否合法。

可以发现,背包的大小最大只有 \(\dfrac{9\times \log_{10}n}{2}\),即 45。而背包的目的也只是需要判断是否合法即可。因此我们用二进制数 \(p\) 来代替背包。若 \(p\) 的 \(2^i\) 是 1,说明在背包中 \(i\) 是可行的。假设向背包中添加一个数 \(x\),只需要 \(p\) 或上 \(p<<x\) 即可。判断的复杂度是 \(\mathcal O(\log n)\)。

但现在问题就在于如何填数使得填出来的数小于等于 \(n\)。

采用数位 \(\text{dp}\) 的思想。枚举当前位填 \(i(0\le i<a_i)\)(\(a_i\) 表示 \(n\) 中第 \(i\) 位的数字),那么剩下位可以随便填。根据公式可推出 \(ans=ans+\frac{(\sum num_i)!}{\Pi(num_i!)}\) (总方案除去相同的数调换顺序的方案)。接着使当前位等于 \(a_i\),向下递归。

这样做我们永远无法取到 \(n\),所以做的是其实求的是 \(1\sim b+1\) 和 \(1\sim a\)。

Code

#include<cstdio>
#include<cstring>
#define N 11
#define ll long long
using namespace std;
ll n,m,ans,cnt,a[N],jc[N],c[N];
void getans(ll x)
{
    if (x>cnt) return;
    for (ll i=0;i<c[cnt-x+1];++i)
        if (a[i])
        {
            a[i]--;
            ll res=0;
            for (ll j=0;j<=9;++j) res+=a[j];
            res=jc[res];
            for (ll j=0;j<=9;++j) res/=jc[a[j]];
            ans+=res;
            a[i]++;
        }
    if (a[c[cnt-x+1]])
    {
        a[c[cnt-x+1]]--;
        getans(x+1);
        a[c[cnt-x+1]]++;
    }
}
void dg(ll xnum,ll xlen,ll sum,ll k)
{
    if (xnum>9)
    {
        if (sum%2==1) return;
        if (a[0]==cnt) return;
        ll p=1;
        for (ll i=0;i<=9;++i)
            for (ll j=1;j<=a[i];++j)
                p|=(p<<(ll)i);
        ll q=((ll)1<<(ll)(sum/2));
        if (!(p&q)) return;
        getans(1);
        return;
    }
    if (xnum==9)
    {
        a[xnum]=cnt-xlen;
        dg(xnum+1,cnt,sum+9*(cnt-xlen),k);
        return;
    }
    for (ll i=0;i<=cnt-xlen;++i)
    {
        a[xnum]=i;
        dg(xnum+1,xlen+i,sum+xnum*i,k);
    }
}
ll calc(ll x)
{
    ll xx=x;
    cnt=0;
    memset(c,0,sizeof(c));
    while (xx)
    {
        c[++cnt]=xx%10;
        xx/=10;
    }
    ans=0;
    memset(a,0,sizeof(a));
    dg(0,0,0,x);
    return ans;
}
int main()
{
    jc[0]=1;
    for (ll i=1;i<=9;++i)
        jc[i]=jc[i-1]*i;
    scanf("%lld%lld",&n,&m);
    while (n||m) printf("%lld\n",calc(m+1)-calc(n)),scanf("%lld%lld",&n,&m);
    return 0;
}

标签:数数,背包,return,ll,cnt,3362,NOI2013,sim,神犇
来源: https://www.cnblogs.com/Livingston/p/16479113.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有