bzoj4980: 第一题

Posted ccz181078

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj4980: 第一题相关的知识,希望对你有一定的参考价值。

Description

神犇xzyo听说sl很弱,于是出了一题来虐一虐sl。一个长度为2n(可能有前缀0)的非负整数x是good的,当且仅当
存在两个长度为n(可能有前缀0)的非负整数a、b满足a+b==10n,并且对于0~9每个数位d,都有Sd(x)==Sd(a)+Sd(
b)(Sd(x)为x的十进制中d出现了多少次)。例如0829是good的,98+02==100。给出一个长度为2n的序列,其中有些
位置是问号。将每个问号替换为0~9任意一个数位后,有多少个good数,答案对1000000007取膜。为了sl不被虐死
,快告诉他怎么写吧。
两个数相加为10^n,意味着可以将数位一部分按09,18,27,36,45配对表示较高位的情况,低位有一对19,28,37,46,55产生了一次进位,更低位可以全0。枚举哪一对数产生进位,可以算出0的出现次数-9的出现次数,1-8,2-7,3-6,4-5的值,然后做一次背包。需要特判一些情况:因为两个0可以配对,0-9的值表示的是0至少比9多几个,实际可以再把一些9换成0,但如果19产生进位,必须留下至少一个9(可能来自已确定的数位,也可能必须由?提供)而不能全部换成0。
#include<bits/stdc++.h>
typedef long long i64;
const int P=1e9+7;
char s[100007];
int n,t[11],m=0,ts[11];
i64 f[1007],g[1007],fac[1007],fiv[1007],ans=0;
i64 _ks[2077][1007],(*ks)[1007]=_ks+1027;
bool d9=0;
void cal(int tp){
    memset(f,0,sizeof(f));
    f[0]=1;
    for(int i=0;i<5;++i){
        memset(g,0,sizeof(g));
        if(ts[i]>m||i&&ts[i]<-m)return;
        if(!i){
            while(ts[i]<-m)ts[i]+=2;
            for(int s=0,d=0;s<=m;++s){
                i64 iv=ks[ts[i]][s];
                if(!iv)continue;
                if(tp==1&&!d9&&(s+ts[i])%2==0)iv=(iv-fiv[s]+P)%P;
                for(int j=s;j<=m;++j)g[j]+=f[j-s]*iv;
                if(++d>6){
                    d=0;
                    for(int j=0;j<=m;++j)g[j]%=P;
                }
            }
        }else for(int a=ts[i],b=0,d=0;a+b<=m;++a,++b)if(a>=0){
            int s=a+b;
            i64 iv=fiv[a]*fiv[b]%P;
            for(int j=s;j<=m;++j)g[j]+=f[j-s]*iv;
            if(++d>6){
                d=0;
                for(int j=0;j<=m;++j)g[j]%=P;
            }
        }
        for(int j=0;j<=m;++j)f[j]=g[j]%P;
    }
    ans=(ans+f[m]*fac[m])%P;
}
i64 pw(i64 a,int n){
    i64 v=1;
    for(;n;n>>=1,a=a*a%P)if(n&1)v=v*a%P;
    return v;
}
int main(){
    for(int i=fac[0]=1;i<=1000;++i)fac[i]=i*fac[i-1]%P;
    fiv[1000]=pw(fac[1000],P-2);
    for(int i=1000;i;--i)fiv[i-1]=i*fiv[i]%P;
    scanf("%s",s);
    n=strlen(s);
    if(n&1)return puts("0"),0;
    for(int i=0;i<n;++i){
        if(s[i]==?)++m;
        else ++t[s[i]-0];
        d9|=s[i]==9;
    }
    for(int i=m;i>=-m;--i){
        for(int a=i,b=0;a+b<=m;++a,++b)if(a>=0)ks[i][a+b]=fiv[a]*fiv[b]%P;
        for(int j=0;j<=m;++j)ks[i][j]=(ks[i][j]+ks[i+2][j])%P;
    }
    for(int i=1;i<=5;++i){
        for(int j=0;j<10;++j)ts[j]=t[j];
        --ts[i],--ts[10-i];
        for(int j=0;j<5;++j)ts[j]=ts[9-j]-ts[j];
        i64 a0=ans;
        cal(i);
        a0=ans-a0;
    }
    printf("%lld\n",ans);
    return 0;
}

 

以上是关于bzoj4980: 第一题的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 4927: 第一题

BZOJ4927第一题 双指针+DP

bzoj 1010 [HNOI2008] 玩具装箱toy (斜率优化第一题)

考后反思(bzoj3940 bzoj4899 bzoj3307)

20151105noip膜你赛bzoj3652 bzoj3653

第一题代码