没有题目链接,数据网上也没有。粘一下题面。
【题目描述】
中国人喜欢数字6和8。特别地,一些人喜欢满足含有特定个数6和8的数。现在请求出,在区间[L,R]之间的第K大的含有X个6和Y个8的数。
【输入】
输入的第一行包括4个数字,L,R,X,Y。
接下来的一行给出该组数据的询问数Q。
接下来Q行中,每行有一个整数K。
【输出】
对于某个询问,输出一行,为对应的第K大的数。如果不存在这个数则输出“That's too bad!”
【输入样例】
1 1000 1 1
10
1
2
3
4
5
6
7
8
9
100
【输出样例】
68
86
168
186
268
286
368
386
468
That's too bad!
【数据范围】
对于30%的数据,1<=L<=R<=100000
对于100%的数据,1<=L<=R<=10^18
对于100%的数据,1<=X,Y<=18, 1<=Q<=30
好像就是水数位DP(Orz)。先粘一下过了30%数据的搜索版。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<map>
using namespace std;
#define LL long long
const int maxn=20;
LL L,R,X,Y;
int cnt=0;
map<LL,int>q;
int limit[maxn];
LL ans[1000000];
inline LL read(){
LL x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void ask(int x,int nx,int ny,int fp,int col,LL num){
if(x==0){
if(nx==X && ny==Y){
if(!col)q[num]=1;
if(col && q[num]!=1)ans[++cnt]=num;
}
return ;
}
int f=fp?limit[x]:9;
for(int i=0;i<=f;i++){
if(i==6)ask(x-1,nx+1,ny,i==limit[x] && fp,col,num*10+i);
else if(i==8)ask(x-1,nx,ny+1,i==limit[x] && fp,col,num*10+i);
else ask(x-1,nx,ny,i==limit[x] && fp,col,num*10+i);
}
}
void find(LL x,int col){
int tot=0;
while(x){
limit[++tot]=x%10; x/=10;
}
ask(tot,0,0,1,col,0);
}
int main(){
L=read(); R=read(); X=read(); Y=read();
find(L,0);
find(R,1);
int Q; scanf("%d",&Q);
for(int i=1;i<=Q;i++){
LL K=read();
if(K>cnt)printf("That's too bad!\n");
else printf("%I64d\n",ans[(int)K]);
}
return 0;
}
再粘一个100%的数据的递推版。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
const int maxn=20;
LL L,R;
int X,Y,len;
int c[maxn][maxn],a[maxn];
void init(){//预处理,数组c[i][j]表示长度为i含有j个符合条件的数的方案数
for(int i=0;i<20;i++){
c[i][i]=1; c[i][0]=1;
}
for(int i=2;i<20;i++)
for(int j=1;j<i;j++){
c[i][j]=c[i-1][j]+c[i-1][j-1];
}
}
void limit(LL N){//求数N的每一位的限制
len=0;
while(N){
a[len++]=N%10; N/=10;
}
reverse(a,a+len);
}
LL power(int x,int y){//快速幂
LL ret=1;
for(LL i=x;y;y>>=1,i*=i){
if(y&1)ret*=i;
}
return ret;
}
LL get_sum(int pos,int x,int y){
if(x<0 || y<0)return 0;
if(pos<x+y)return 0;
return c[pos][x+y]*c[x+y][x]*power(8,pos-x-y);
/*
方案数为:pos个数中选x+y个6和8,然后在x+y个数中选x个6,这样6和8就选好了。剩下的就只需选除去6和8的数
那就是8^(pos-x-y),快速幂求出。
*/
}
LL get(LL N){
limit(N);
LL ret=0;
int x=X,y=Y;
for(int i=0;i<len && x>-1 && y>-1;i++){
if(a[i]<7)ret+=(a[i])*get_sum(len-i-1,x,y);
else if(a[i]<9)ret+=(a[i]-1)*get_sum(len-i-1,x,y)+get_sum(len-i-1,x-1,y);
else ret+=(a[i]-2)*get_sum(len-i-1,x,y)+get_sum(len-i-1,x-1,y)+get_sum(len-i-1,x,y-1);
x-=(a[i]==6); y-=(a[i]==8);
}
return ret;
}
LL get_num(LL N){
LL num=0,ret=0;
int pos=0,x=X,y=Y;
while(get_sum(pos,X,Y)<N)pos++;
for(int i=0;i<pos;i++){
int j;
for(j=0;j<9 && num+get_sum(pos-i-1,x-(j==6),y-(j==8))<N;j++){
num+=get_sum(pos-i-1,x-(j==6),y-(j==8));
}
x-=(j==6); y-=(j==8);
ret=ret*10+j;
}
return ret;
}
int main(){
init();
scanf("%I64d%I64d%d%d",&L,&R,&X,&Y);
LL lx=get(L); LL ly=get(R+1);
int Q; scanf("%d",&Q);
for(int i=1;i<=Q;i++){
LL temp; scanf("%I64d",&temp);
if(temp+lx>ly)printf("That's too bad!\n");
else printf("%I64d\n",get_num(temp+lx));
}
return 0;
}