P14094 [ICPC 2023 Seoul R] Special Numbers
数位 DP。
考虑使用 \(f[pos][g]\) 记忆化,其中:
- \(pos\) 表示当前填到第几位。
- \(g\) 表示填过位置的乘积与 \(k\) 的 \(\gcd\)。
根据这个表格我们知道,\(10^{17}\) 内的因数最多的数[1]只有不到 \(65536\) 个因数。所以 \(g\) 的取值不超过 \(65536\) 种,将第二维离散化一下就可以了。
代码中,第二位是用哈希表(gp_hash_table)现用现算的,常数不小,但是目前谷上最优解。
点击查看代码
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define int long long
using namespace std;
using namespace __gnu_pbds;
const int N=25,P=1e9+7;
int k,len,a[N],f[N][65536],idx;
string l,r;
gp_hash_table<int,int> ma;
inline int gcd(__int128 a,int b){int r=a%b;while(r) a=b,b=r,r=a%b;return b;
}
inline int dfs(int p,__int128 fc,bool zro,bool lim){int g=gcd(fc,k),gg;if(!p) return g==k;if(ma.find(g)==ma.end()) ma[g]=gg=idx++;else gg=ma[g];if(!zro&&!lim&&(~f[p][gg])) return f[p][gg];int rig=lim?a[p]:9,ans=0;for(int i=0;i<=rig;i++){bool tzro=zro&&!i;ans+=dfs(p-1,tzro?1:fc*i,tzro,lim&&i==rig);}ans%=P;if(!zro&&!lim) f[p][gg]=ans;return ans;
}
inline int solve(string& s){len=s.size();for(int i=0;i<len;i++) a[len-i]=s[i]-'0';return dfs(len,1,1,1);
}
inline bool check(string& s){int f=1;for(int i:s) if(!((f*=i-'0')%=k)) return 1;return 0;
}
signed main(){memset(f,-1,sizeof f);cin>>k>>l>>r;cout<<(solve(r)-solve(l)+check(l)+P)%P<<"\n";//字符串-1不好做,左端点单独处理(其实int128就可以)return 0;
}
另一种做法是考虑到 \(k\) 若有某个超过 \(10\) 的质因数,则一定无解。因此只需要用质因子 \(2,3,5,7\) 的指数进行记忆化即可。
即 Highly Composite Numbers,表格数据来源。 ↩︎