您的位置:首页 > 其它

幸运数字 (数位DP)

2015-03-18 17:04 211 查看
没有题目链接,数据网上也没有。粘一下题面。

【题目描述】

中国人喜欢数字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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: