题目地址:http://acm.uestc.edu.cn/problem.php?pid=1689&cid=167
按位dp+二分。
先按位dp出一个数组:dp[loc][k][k1][k2];
表示有loc位,最高位为k,低位全为0的数M,区间[0,M]内含有k1个6,k2个8的数字的个数。比如dp[10][3][2][4]表示区间[0,3000000000]内,有2个6,4个8的数的个数。
求[0,L]内的有k1个6,k2个8的数的个数可以利用dp矩阵组合求解。
先求出[0,L-1]内有X个6和Y个8的数的个数n1,[0,R]内有X个6和Y个8的数的个数n2,于是要求的数num是满足[0,num]内有n1+K的最小num。于是在[L,R]间二分求解。
#include<cstdio> #include<cstring> #include<cstdlib> #include<string> #include<cmath> #include<iostream> #include<algorithm> #include<map> #include<vector> #include<queue> #define sf scanf #define pf printf #define pfn printf("\n"); #define ll long long #define INF 0x7fffffff using namespace std; ll L,R,x,y,q,k,ans,n1,n2,temp; ll dp[18][10][19][19];//有loc位,最高位为k,低位全为0的数M,区间[0,M]内含有k1个6,k2个8的数字的个数。 ll length(ll n){ ll len=0; if(n==0) return 0; while(n){ n/=10; len++; } return --len; } void DP(){ ll loc,i,k1,k2,t1,t2,j; for(loc=0; loc<18; loc++) for(i=0; i<=9; i++) for(k1=0; k1<=18; k1++) for(k2=0; k2<=18; k2++) dp[loc][i][k1][k2]=0; //construct the kernel dp[0][0][0][0]=1; for(i=1; i<=9; i++){ dp[0][i][0][0]=dp[0][i-1][0][0]; dp[0][i][1][0]=dp[0][i-1][1][0]; dp[0][i][0][1]=dp[0][i-1][0][1]; if(i!=6 && i!=8) dp[0][i][0][0]++; else if(i==6) dp[0][i][1][0]++; else if(i==8) dp[0][i][0][1]++; } //states trans for(loc=1; loc<18; loc++){ for(k1=0; k1<=loc+1; k1++) for(k2=0; k2+k1<=loc+1; k2++){ for(i=1; i<=9; i++){ dp[loc][i][k1][k2]=dp[loc][i-1][k1][k2]; t1=k1; t2=k2; j=loc-1; if(i==7) t1--; if(i==9) t2--; while(j>=0){ dp[loc][i][k1][k2]+=dp[j][9][t1][t2]; j--; } } } } } ll find(ll k2,ll x,ll y,ll len){ ll k=0,num=0; while(k2){ k*=10; k+=k2%10; k2/=10; } while(len>=0){ num+=dp[len][k%10][x][y]; if(k%10==6) x--; //if(x<0) x=0; if(k%10==8) y--; //if(y<0) y=0; if(x<0 || y<0) break; k/=10; len--; } return num; } void D(ll L, ll R,ll x,ll y,ll k){ ll mid=(L+R)/2,t,t1,t2; if(L==R){ ans=L; return ; } t=find(mid,x,y,length(mid)); if(t<k) D(mid+1,R,x,y,k); else D(L,mid,x,y,k); } int main() { ll T,i,t=0; cin>>T; temp=1; for(i=1; i<=18; i++) temp*=10; DP(); while(T--){ t++; printf("Case #%lld:\n",t); sf("%lld %lld %lld %lld %lld",&L,&R,&x,&y,&q); if(R==temp) R--; if(L==temp) L--; if(x<=18 && y<=18){ n1=find(L-1,x,y,length(L-1)); n2=find(R,x,y,length(R)); } for(i=0; i<q; i++){ sf("%lld",&k); if(n1+k>n2 || x>18 || y>18) cout<<"That's too bad!\n"; else{ D(L,R,x,y,n1+k); cout<<ans<<endl; } } } return 0; }