沒有題目鏈接,數據網上也沒有。粘一下題面。
【題目描述】
中國人喜歡數字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;
}