HDU 7060 Separated Number (字符串+组合数学)

链接

题意:

给出一个数位为 n n n的数字 ( n < = 1 e 6 ) ( n < = 1 e 6 ) (n<=1e6),现在可以将该数字最多分成 k ( 1 < = k < = n ) k ( 1 < = k < = n) k(1<=k<=n)段。定义一种分法的贡献为所有段的数字之和,求所有分法的贡献和。

例如对于样例,答案为 1 + 1 + 10 + 100 = 112 1 + 1 + 10 + 100 = 112 1+1+10+100=112

分析:

首先我们肯定算每位数字对答案产生的贡献。
但是我们能够发现每一位做出的贡献从x,x10,x100,x*1000,这个会很影响我们思考问题的结果。

如果我们看一个序列 . . . . i j . . . ....ij... ....ij..., i i i处于 x x x位上,那么 j j j处于 x − 1 x-1 x1位上(我们从各位开始记数)。那么 j j j可以做的位(也就是乘的10的次方) i i i也一定可以做,并且 i i i还比 j j j多一个 1 0 i 10^i 10i.有点绕慢慢理解。
举个例子。123456。
抛开几段的限制
6的贡献只能是61
5的贡献可以是5
(1+10)
4的贡献是4*(1+10+100)

这样是不是好理解点了。

然后我们看 k k k段的限制。如果我们看当前 i i i位值贡献计算 1 0 j 10^j 10j那么在 i + j i+j i+j位置一定有一个挡板也就是从此分段,那么有三种情况。

  1. i + j > n i+j>n i+j>n贡献为0,越位了。
  2. i + j = = n i+j==n i+j==n贡献是 ∑ i = 0 k − 1 C n − j − 1 i \sum_{i=0}^{k-1}C_{n-j-1}^{i} i=0k1Cnj1i ( C n − j − 1 i C_{n-j-1}^{i} Cnj1i表示确定了j位置,在剩下的{n-j-1}中找出i段,为什么是(k-1)哪?i+j=n这最后算一段)
  3. i + j < n i+j<n i+j<n贡献是 ∑ i = 0 k − 2 C n − j − 1 i \sum_{i=0}^{k-2}C_{n-j-1}^{i} i=0k2Cnj1i (为什么上面是 k − 1 k-1 k1这个地方又成了 k − 2 k-2 k2,因为上面是分出来后,那一段就是端点,而这个是一切三段,固定中间1段。)

那么我们就需要预处理出来 ∑ i = 0 k − 1 C n − j − 1 i \sum_{i=0}^{k-1}C_{n-j-1}^{i} i=0k1Cnj1i.

∑ i = 0 x C n i = ∑ i = 0 x − 1 C n i + C n x \sum_{i=0}^{x}C_{n}^{i}=\sum_{i=0}^{x-1}C_{n}^{i}+C_{n}^{x} i=0xCni=i=0x1Cni+Cnx
这个递推式子可以实现。

我们看组合数定理:
C n m = C n − 1 m + C n − 1 m − 1 C_n^m=C_{n-1}^m+C_{n-1}^{m-1} Cnm=Cn1m+Cn1m1
∑ i = 0 x C n i = C n 0 + C n 1 + C n 2 . . . + C n x \sum_{i=0}^{x}C_{n}^{i}=C_{n}^{0}+C_{n}^{1}+C_{n}^{2}...+C_{n}^{x} i=0xCni=Cn0+Cn1+Cn2...+Cnx
利用上面的组合数定理:
C n − 1 − 1 + C n − 1 0 + C n − 1 0 + C n − 1 1 + C n − 1 1 + . . . . + C n − 1 x − 1 + C n − 1 x − 1 + C ( n − 1 ) x C_{n-1}^{-1}+C_{n-1}^{0}+C_{n-1}^{0}+C_{n-1}^{1}+C_{n-1}^{1}+....+C_{n-1}^{x-1}+C_{n-1}^{x-1}+C_(n-1)^{x} Cn11+Cn10+Cn10+Cn11+Cn11+....+Cn1x1+Cn1x1+C(n1)x
= 2 ∑ i = 0 x C n − 1 i − C n − 1 x =2\sum_{i=0}^{x}C_{n-1}^{i}-C_{n-1}^{x} =2i=0xCn1iCn1x

递推式子也有了。


ll qpow(ll a, ll b)
{
    ll ans = 1;
    while(b)
    {
        if(b & 1) ans = ans * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return ans;
}
ll n, k;
ll qpow10[maxn], a[maxn], b[maxn];
ll fac[maxn];
void init()
{
    fac[0] = qpow10[0] = 1;
    for(int i = 1; i < maxn; i++)
    {
        fac[i] = fac[i - 1] * i % mod;
        qpow10[i] = qpow10[i - 1] * 10 % mod;
    }
}
ll infac(ll x)
{
    return qpow(x, mod - 2);
}
ll C(ll n, ll m)
{
    if(n < m) return 0;
    return fac[n] * infac(fac[m]) % mod * infac(fac[n - m]) % mod;
}
//string str;
char str[maxn];
void solve()
{
    scanf("%lld %s",&k,str+1);
    n=strlen(str+1);    
    a[0] = (k - 2 >= 0) ? 1 : 0;
    b[0] = (k - 1 >= 0) ? 1 : 0;
    for(ll i = 1; i < n; i++)
    {
        a[i] = (2 * a[i - 1] - C(i - 1, k - 2) + mod) % mod;
        b[i] = (2 * b[i - 1] - C(i - 1, k - 1) + mod) % mod;
    }
    ll ans = 0, sum = 0;
    for(ll i = n; i >= 1; i--)
    {
        ll tmp = str[i] - '0';
        if(i != n)
        {
            sum = (sum + a[i - 1] * qpow10[n - i - 1]) % mod;
        }
        ans = (ans + tmp * (b[i - 1] * qpow10[n - i] % mod + sum)) % mod;
    }
    cout << ans << endl;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值