[Lucas+数位DP] 2015 计蒜之道 复赛 C. 360的产品试用体验

就是Lucas+数位DP的套路。这题和一般的数位DP相比有个特别的限制是 x1+x2+x3n ,类似HNOI 2007《梦幻岛宝珠》的处理方法,即相当于借位,保存当前这位剩余的数,转移时乘P传给下一位。
有3个数要填,一开始我写的是每次转移一位,转移要 O(473) ,结果T了。实际上应该每次转移一个数的一位,就好了。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
inline char gc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline LL getLL(){
    char ch=gc(); LL res=0; 
    while(!('0'<=ch&&ch<='9')) ch=gc();
    while('0'<=ch&&ch<='9') res=(res<<3)+(res<<1)+ch-'0', ch=gc();
    return res;
}
const int maxn=20,P=47;
int _test,a1[maxn],a2[maxn],a3[maxn],R1[maxn],R2[maxn],R3[maxn],N[maxn];
int f[maxn][3][2][2][2][3*47+5];
LL n,L[4],R[4];
int ans,fac[P+5],inv[P+5],fac_inv[P+5];
inline int C(int n,int m){ if(n<m) return 0; return fac[n]*fac_inv[m]%P*fac_inv[n-m]%P; }
int Solve(LL x,LL y,LL z){
    memset(f,0,sizeof(f));memset(R1,0,sizeof(R1));memset(R2,0,sizeof(R2));memset(R3,0,sizeof(R3));
    R1[0]=0; do R1[++R1[0]]=x%P, x/=P; while(x);
    R2[0]=0; do R2[++R2[0]]=y%P, y/=P; while(y);
    R3[0]=0; do R3[++R3[0]]=z%P, z/=P; while(z);
    int m=max(max(N[0],R1[0]),max(R2[0],R3[0]));
    f[m+1][2][0][0][0][0]=1;
    for(int i=m+1;i>=1;i--)
     for(int j=0;j<=2;j++) if(!(i==1&&j==2))
      for(int j1=0;j1<=1;j1++)
       for(int j2=0;j2<=1;j2++)
        for(int j3=0;j3<=1;j3++)
         for(int k=0;k<=47*3;k++) if(f[i][j][j1][j2][j3][k]){ 
             int _f=f[i][j][j1][j2][j3][k];
            if(j==0){
                for(int x=0;x<=(j2?P-1:R2[i]);x++) if(x<=k)
                 (f[i][1][j1][j2|(x<R2[i])][j3][k-x]+=_f*C(a2[i],x))%=P;
            }
            if(j==1){
                for(int x=0;x<=(j3?P-1:R3[i]);x++) if(x<=k)
                 (f[i][2][j1][j2][j3|(x<R3[i])][k-x]+=_f*C(a3[i],x))%=P;                
            } 
            if(j==2){
                for(int x=0;x<=(j1?P-1:R1[i-1]);x++) if(x<=k*P+N[i-1])
                 (f[i-1][0][j1|(x<R1[i-1])][j2][j3][min(k*P+N[i-1]-x,47*3)]+=_f*C(a1[i-1],x))%=P;               
            }
         }
    int res=0;
    for(int j1=0;j1<=1;j1++)
     for(int j2=0;j2<=1;j2++)
      for(int j3=0;j3<=1;j3++)
       for(int k=0;k<=47*3;k++) (res+=f[1][2][j1][j2][j3][k])%=P;
    return res;
}
int main(){
    freopen("jsk445.in","r",stdin);
    freopen("jsk445.out","w",stdout);
    fac[0]=1; for(int i=1;i<=P;i++) fac[i]=fac[i-1]*i%P;
    inv[1]=1; for(int i=2;i<=P;i++) inv[i]=(P-P/i)*inv[P%i]%P;
    fac_inv[0]=1; for(int i=1;i<=P;i++) fac_inv[i]=fac_inv[i-1]*inv[i]%P;
    _test=getLL();
    while(_test--){
        memset(N,0,sizeof(N));memset(a1,0,sizeof(a1));memset(a2,0,sizeof(a2));memset(a3,0,sizeof(a3)); 
        LL x,y,z; x=getLL(),L[1]=getLL(),R[1]=getLL(),y=getLL(),L[2]=getLL(),R[2]=getLL(),z=getLL(),L[3]=getLL(),R[3]=getLL(),n=getLL();
        N[0]=0; do N[++N[0]]=n%P, n/=P; while(n);
        a1[0]=0; do a1[++a1[0]]=x%P, x/=P; while(x);
        a2[0]=0; do a2[++a2[0]]=y%P, y/=P; while(y);
        a3[0]=0; do a3[++a3[0]]=z%P, z/=P; while(z);
        ans=Solve(R[1],R[2],R[3]);
        (ans-=Solve(L[1]-1,R[2],R[3]))%=P;
        (ans-=Solve(R[1],L[2]-1,R[3]))%=P;
        (ans-=Solve(R[1],R[2],L[3]-1))%=P;
        (ans+=Solve(L[1]-1,L[2]-1,R[3]))%=P;
        (ans+=Solve(L[1]-1,R[2],L[3]-1))%=P;
        (ans+=Solve(R[1],L[2]-1,L[3]-1))%=P;
        (ans-=Solve(L[1]-1,L[2]-1,L[3]-1))%=P;
        printf("%d\n",(ans+P)%P); 
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值