51nod 1528 加号分配

题意

现在要给一个长度为n数字串上面加上恰好k个加号,把所有可能的算术结果相加起来。
加号加到数字串中间之后要形成正确的算术表达式。规则是:没有两个加号连在一起,两个加号之间至少要有一位数字,加号不能加在开头,也不能加在结尾。比如数字串是10500,那么100500(加0个加号),1+00+500 或者 10050+0 这些放置的加号都是合法的,而100++500, +1+0+0+5+0+0 和100500+都是非法的。
结果比较大,对 109+7 取余输出即可。

题解

不难的题,想了一会就想出来了,哈哈

O(n^2)的做法

枚举一个区间
然后排列组合算次数

O(n)的做法

考虑每一位对于答案的贡献
也就是第i位,,他是 100,101,102....10k 10 0 , 10 1 , 10 2 . . . .10 k 分别有多少种情况
然后发现,除了这一位所能表示的最大,别的都是常数啊
然后这个可以预处理,最后一个判断一下就可以了

CODE:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long LL;
const LL MOD=1e9+7;
const LL N=1000005;
LL n,k;
LL JC[N],inv[N];
LL sum=0;
LL pow (LL x,LL y)
{
    if (y==1) return x;
    LL lalal=pow(x,y>>1);
    lalal=lalal*lalal%MOD;
    if (y&1) lalal=lalal*x%MOD;
    return lalal;
}
LL lalal[N];//前缀和 
LL C (LL x,LL y)
{   
    if (x<y) return 0;
    if (y==0) return 1;
    if (x==y) return 1;
    return JC[x]*inv[y]%MOD*inv[x-y]%MOD;
}
int main()
{
    scanf("%lld%lld",&n,&k);
    JC[1]=1;for (LL u=2;u<=n;u++) JC[u]=JC[u-1]*u%MOD;
    inv[n]=pow(JC[n],MOD-2);for (LL u=n-1;u>=1;u--) inv[u]=inv[u+1]*(u+1)%MOD;
    LL o=1;
    for (LL u=1;u<=n;u++) 
    {
    //  printf("%lld %lld %lld\n",n-u,k-1,C(n-u,k-1));
        lalal[u]=lalal[u-1]+C(n-u-1,k-1)*o%MOD;
        lalal[u]%=MOD;
        o=o*10%MOD;
    }
    LL ans=0;
    o=1;
    for (int u=1;u<n;u++) o=o*10%MOD;
    LL INV=pow(10,MOD-2);
    for (LL u=1;u<=n;u++)
    {
        char ch=getchar();
        while (ch<'0'||ch>'9') ch=getchar();
        ans=ans+lalal[n-u]*(ch-'0');
        ans=ans+C(u-1,k)*o%MOD*(ch-'0');
        ans%=MOD;
        /*printf("%lld\n",lalal[n-u]);
        printf("%lld\n",ans);*/
        o=o*INV%MOD;
    }
    printf("%lld\n",ans);
    return 0;
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值