1. 程式人生 > >[BZOJ1974][SDOI2010]程式碼拍賣會[插板法]

[BZOJ1974][SDOI2010]程式碼拍賣會[插板法]

題意

詢問有多少個數位為 \(n\) 的形如 \(11223333444589\) 的數位值不下降的數字在\(\mod p\) 的意義下同餘 \(0\)

$n\leq 10^{18} ,p\leq 500 $ 。

分析

  • 考慮普通的狀態,矩乘和考慮每種數字選擇什麼都沒法做,要另闢蹊徑。

  • 發現這樣的數字都可以拆分成1~9個形如 \(111111\) 的形式,記為 \(\rm gg\)

  • 考慮算出所有此類數字在\(\mod p\) 意義下餘數為 \(x\) 的有多少個。

  • 狀態呼之欲出: \(f_{i,j,k}\) 表示考慮到 \(\rm gg\) 餘數為 \(i\) 的 ,總的餘數為 \(j\)

    ,已經選擇了 \(k\)\(\rm gg\) 的方案總數。

  • 轉移列舉 \(\rm gg\) 餘數為 \(i\) 的選擇了多少個,注意這類 \(\rm gg\) 的選擇是組合而不是排列,考慮插板法算方案。

  • 總時間複雜度為\(O(10^2*p^2)\)

可重集的排列變組合可以考慮插板法。

程式碼

#include<bits/stdc++.h>
using namespace std;
#define go(u) for(int i=head[u],v=e[i].to;i;i=e[i].last,v=e[i].to)
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define pb push_back
typedef long long LL;
inline int gi(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
    return x*f;
}
template<typename T>inline bool Max(T &a,T b){return a<b?a=b,1:0;}
template<typename T>inline bool Min(T &a,T b){return b<a?a=b,1:0;}
const int N=504,mod=999911659;
LL st,n,p,rev[N],cnt[N],f[N][N][10],inv[N];
int pos[N];
void add(LL &a,LL b){a+=b;if(a>=mod) a-=mod;}
LL C(LL n,LL m){
    LL res=1ll;
    for(LL i=n-m+1;i<=n;++i) res=i%mod*res%mod;
    for(LL i=2;i<=m;++i) res=res*inv[i]%mod;
    return res;
}
int main(){
    scanf("%lld%lld",&n,&p);
    inv[1]=1;
    for(int i=2;i<=500;++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
    memset(pos,-1,sizeof pos);
    pos[0]=0,rev[0]=0,cnt[0]=1;LL v=1%p;
    for(LL i=1;i<=min(n,p);++i){
        if(pos[v]!=-1){
            LL len=i-pos[v],a=(n-i+1)/len,b=(n-i+1)%len;
            st=rev[pos[v]+(b-1+len)%len];
            for(int j=pos[v];j<i;++j) cnt[rev[j]]+=a+(j-pos[v]+1<=b);
            break;
        }else if(i==n) st=v;
        pos[v]=i,rev[i]=v,cnt[v]++;
        v=(v*10+1)%p;
    }
    rep(k,0,8) f[0][st][k]=C(cnt[0]+k-1,k);
    
    rep(i,1,p-1)
    rep(j,0,p-1)
    rep(k,0,8){
        f[i][j][k]=f[i-1][j][k];
        rep(h,1,k)
        add(f[i][j][k],f[i-1][((j-h*i)%p+p)%p][k-h]*C(cnt[i]+h-1,h)%mod);
    }
    printf("%lld\n",f[p-1][0][8]);
    return 0;
}