幸運數字 (數位DP)

沒有題目鏈接,數據網上也沒有。粘一下題面。

【題目描述】

中國人喜歡數字68。特別地,一些人喜歡滿足含有特定個數68的數。現在請求出,在區間[L,R]之間的第K大的含有X6Y8的數。

【輸入】

輸入的第一行包括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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章