[BZOJ1974][SDOI2010]代碼拍賣會[插板法]
阿新 • • 發佈:2018-10-19
amp ace print def int space memset last pre
題意
詢問有多少個數位為 \(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\) 余數為 \(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; }
[BZOJ1974][SDOI2010]代碼拍賣會[插板法]