bzoj2757【scoi2012】Blinker的仰慕者
题目描述
Blinker 有非常多的仰慕者,他给每个仰慕者一个正整数编号。而且这些编号还隐藏着特殊的意义,即编号的各位数字之积表示这名仰慕者对Blinker的重要度。 现在Blinker想知道编号介于某两个值A,B之间,且重要度为某个定值K的仰慕者编号和。
输入格式
输入的第一行是一个整数N,表示Blinker想知道的信息个数。
接下来的N行,每行有三个数,A,B,K。表示 Blinker想知道编号介于A和B之间的,
重要度为K的仰慕者的编号和。
输出格式
输出N行,每行输出介于A和B之间,重要度为 K的仰慕者编号和。结果可能很大,
模上20120427。
提示
【数据范围】
对于20%的数据,保证: 2<=A<=B<=1000000000,1<=N<=30
对于50%的数据,保证:2<=A<=B<=1000000000000000000,1<=N<=30
对于100%的数据,保证: 2<=A<=B<=1000000000000000000,1<=N<=5000
-
题解:
- 由于因子只有2 3 5 7 , 状态大概有3e4+,不超过4e4;
- 用HASH_TABLE存下来;
- 多组询问,先预处理$i$位的答案;
- 对每组询问$dfs$统计答案,如果没有前导零并且没有抵达上界则直接返回,否则进一步$dfs$
- 代码不太好些,注释里有关于变量的说明;
- 注意$K=0$和$K \neq 0$最好分开统计;
- $n$为位数上界,时间复杂度:$O(T*10n +n*4e4*10)$
1 #include<bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 const int N=20,M=4e4,sz=1234561,mod=20120427; 5 ll A,B,K; 6 int cnt,a[N],pw[N]; 7 int o,hd[sz],nt[sz];ll v[sz]; 8 void upd(int&x,int y){x+=y;if(x>=mod)x-=mod;} 9 void add(ll x){ 10 int u=x%sz; 11 for(int i=hd[u];i;i=nt[i])if(v[i]==x)return; 12 nt[++o]=hd[u],hd[u]=o,v[o]=x; 13 } 14 int find(ll x){ 15 int u=x%sz; 16 for(int i=hd[u];i;i=nt[i])if(v[i]==x)return i; 17 return 0; 18 }//HASH表 19 void dfs(int x,int y,ll z){ 20 if(x==18)add(z); 21 else{ 22 if(!y)return; 23 for(int i=x;i<=18;++i){ 24 dfs(i,y-1,z); 25 z*=y; 26 } 27 } 28 }//dfs方案 29 namespace Solve{ 30 int f[N][M],g[N][M];//f[位数][乘积] 31 void init(){ 32 f[0][1]=1; 33 for(int i=0;i<18;++i) 34 for(int j=1;j<=o;++j) 35 if(f[i][j]){ 36 for(int l=0;l<=9;++l){ 37 int y=find(v[j]*l); 38 upd(f[i+1][y],f[i][j]); 39 upd(g[i+1][y],(g[i][j]*10+f[i][j]*l)%mod); 40 } 41 } 42 }//预处理i位无限制的答案 43 int cal1(int i,int j,int k,ll x,ll y){//已经枚举好的位数,前导零,上界,前i位的值,当前K 44 if(i==cnt)return y==1?x:0; 45 if(!k&&!j)return (1ll*x*pw[cnt-i]%mod*f[cnt-i][find(y)]%mod+g[cnt-i][find(y)])%mod; 46 int re=0,l=j?0:1,r=k?a[i+1]:9; 47 for(int i1=l,t;i1<=r;++i1)if(!i1||y%i1==0){ 48 ll y1=!i1?y:y/i1; 49 upd(re, t=cal1(i+1,j&&!i1,k&&i1==a[i+1],(x*10+i1)%mod,y1)); 50 } 51 return re; 52 } 53 int cal0(int i,int j,int k,ll x,ll y){//前四个同cal1,y为前i为是否出现0 54 if(i==cnt)return !y?x:0; 55 if(!k&&!j){ 56 if(!y)return (1ll*x*pw[cnt-i]%mod*pw[cnt-i]%mod+1ll*(pw[cnt-i]-1)*pw[cnt-i]/2%mod)%mod;//后面cnt-i任意; 57 else return 1ll*x*pw[cnt-i]%mod*f[cnt-i][o]%mod+g[cnt-i][o];//后面cnt-i为乘积为0; 58 } 59 int re=0,l=0,r=k?a[i+1]:9; 60 for(int i1=l;i1<=r;++i1){ 61 int y1=j&&!i1?1:y*i1; 62 upd(re, cal0(i+1,j&&!i1,k&&i1==a[i+1],(x*10+i1)%mod,y1)); 63 } 64 return re; 65 } 66 //注意理解一下两个方面: 67 //是如何统计前i位达到上界,i+1位不是上界的答案的;(状态k) 68 //是如何统计位数小于上界位数cnt的答案的; (状态j) 69 //上界是如何被统计的(line44) 70 } 71 int main(){ 72 #ifndef ONLINE_JUDGE 73 freopen("T2.in","r",stdin); 74 freopen("T2.out","w",stdout); 75 #endif 76 dfs(0,9,1);add(0); 77 Solve::init(); 78 for(int i=pw[0]=1;i<=18;++i)pw[i]=pw[i-1]*10%mod; 79 int T;scanf("%d",&T); 80 while(T--){ 81 int ans=0; 82 scanf("%lld%lld%lld",&A,&B,&K);A--; 83 cnt=0;while(A)a[++cnt]=A%10,A/=10; 84 for(int i=1;i<=cnt>>1;++i)swap(a[i],a[cnt-i+1]); 85 ans-= K?Solve::cal1(0,1,1,0,K) : Solve::cal0(0,1,1,0,1); 86 cnt=0;while(B)a[++cnt]=B%10,B/=10; 87 for(int i=1;i<=cnt>>1;++i)swap(a[i],a[cnt-i+1]); 88 ans+= K?Solve::cal1(0,1,1,0,K) : Solve::cal0(0,1,1,0,1); 89 printf("%d\n",(ans%mod+mod)%mod); 90 } 91 return 0; 92 }