题目大意:给定区间[l,r]、x、y,求出在这个区间内含有x个4,y个7的第K大的数,否则输出Nya!
题目分析:参照先统计在计数的思想。设dp[i][j][k]表示第i位有j个4,k个7的数字有几个。显然对于dp[i-1][j][k],可以转移到dp[i][j+1][k]和dp[i][j][k+1]。对于另外8个数(除4,7)有dp[i][j][k]=dp[i-1][j][k]*8. 这样就完成了计数。统计的思想不再阐述。
处理完了计数考虑第K大,直接使用二分处理了。
细节上还是要注意开区间,否则可能会wa。
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #include<functional> #include<cmath> #include<cctype> #include<cassert> #include<climits> using namespace std; #define For(i,n) for(int i=1;i<=n;i++) #define Rep(i,n) for(int i=0;i<n;i++) #define Fork(i,k,n) for(int i=k;i<=n;i++) #define ForD(i,n) for(int i=n;i;i--) #define Forp(x) for(int p=pre[x];p;p=next[p]) #define RepD(i,n) for(int i=n;i>=0;i--) #define MEM(a) memset(a,0,sizeof(a)) #define MEMI(a) memset(a,127,sizeof(a)) #define MEMi(a) memset(a,128,sizeof(a)) #define INF (2139062143) #define phiF (1000000007) #define MAXN (1000000+10) typedef long long LL; LL dp[25][25][25],digit[50],x,y; inline void calc(){ dp[0][0][0]=1; For(i,21){ Rep(j,i) Rep(k,i) if (j+k<i){ dp[i][j][k+1]+=dp[i-1][j][k]; dp[i][j+1][k]+=dp[i-1][j][k]; dp[i][j][k]+=dp[i-1][j][k]*8; } } } inline LL work(LL num){ int len=0; while (num){ digit[++len]=num%10; num/=10; } int ux=x,uy=y; LL ans=0; ForD(i,len){ Rep(j,digit[i]){ if (j==4) { if (ux) ans+=dp[i-1][ux-1][uy]; } if (j==7){ if (uy) ans+=dp[i-1][ux][uy-1]; } if (j!=4&&j!=7) ans+=dp[i-1][ux][uy]; } if (digit[i]==4) ux--; if (digit[i]==7) uy--; if (ux<0||uy<0) break; } return ans; } int main(){ freopen("test.in","r",stdin); calc(); int T; LL l,r;int n; scanf("%d",&T); For (q,T){ printf("Case #%d:\n",q); scanf("%I64d%I64d%d%d",&l,&r,&x,&y); scanf("%d",&n); LL ll=work(l+1); LL rr=work(r+1); For (i,n){ int t; scanf("%d",&t); if (t>rr-ll) { printf("Nya!\n"); continue; } t+=ll; LL ul(l+1),ur(r); while (ul<ur){ LL mid=(ul+ur)>>1; if (work(mid)>=t) ur=mid;else { ul=mid+1; } } printf("%lld\n",ul-1); } } }