题意:
一个数如果没有任何两位的数字是相同的,则该数称为B数,有两种询问:
- 问[a,b]区间有多少个B数(在10/16进制下)
- 问第k个B数是什么(在10/16进制下)
思路:
显然第一类问题可以用数位DP求解,第二类问题就是在数位dp外面套个二分就可以求解。由于输入的数都是unsigned long long,有一些细节需要注意,比如边界特判等。二分的时候采用左开右闭,因为如果[0,a]存在k个B数,那么此时二分的右边界就是a,且可以取到。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
inline bool test(int x,int y){
if((x>>y)&1)return 1;
else return 0;
}
char domin;
int a[21],len;
ull dp[21][1<<16|1][2][2];
ull dfs(int pos,int limit,int lead,int st,int base){
if(pos==0) return !lead;
if(!limit && dp[pos][st][lead][base==10?0:1]!=-1)
return dp[pos][st][lead][base==10?0:1];
ull ans=0;
int maxx=limit?a[pos]:base-1;
for(int i=0;i<=maxx;i++){
if(!test(st,i)){
ans+=dfs(pos-1,limit&&i==maxx,lead&&i==0,(lead&&i==0)?0:st|(1<<i),base);
}
}
if(!limit) dp[pos][st][lead][base==10?0:1]=ans;
return ans;
}
ull solve1(ull x){
len=0;
int base=domin=='d'?10:16;
while(x){
a[++len]=x%base;
x/=base;
}
return dfs(len,1,1,0,base);
}
void input(ull &a){
if(domin=='d')
scanf("%llu",&a);
else{
a=0;
char ch=getchar();
while(!(ch>='0' && ch<='9')&&!(ch>='a' && ch<='f')) ch=getchar();
while( (ch>='0' && ch<='9')||(ch>='a' && ch<='f') ){
a=a*16;
if(ch>='a')
a+=10+ch-'a';
else
a+=ch-'0';
ch=getchar();
}
}
}
void print(ull a){
if(a==0){
puts("0");return;
}
if(domin=='d'){//10
printf("%llu
",a);return;
}
//16
vector<int>ans;
while(a){
ans.push_back(a%16);
a/=16;
}
for(int i=ans.size()-1;i>=0;i--){
if(ans[i]>=10)
printf("%c",'a'+ans[i]-10);
else
printf("%d",ans[i]);
}
puts("");
}
int main () {
domin='h';
memset(dp,-1,sizeof dp);
int T;
scanf("%d",&T);
while(T--){
ull a,b,i;
scanf(" %c",&domin);
int typ;scanf("%d",&typ);
if(typ==0){
input(a);
input(b);
ull ans=solve1(b);
if(a==0)ans++;
else{
ans-=solve1(a-1);
}
print(ans);
}
else{
input(i);
if(i<9){
printf("%llu
",i-1);
continue;
}
ull l=0,r=0;r--;
if(solve1(r)<i-1){
puts("-");
continue;
}
while(r-l>1){//zkyb
ull mid=(r>>1)+(l>>1);
if((r&1)&&(l&1))mid++;
if(solve1(mid)<i-1)
l=mid;
else
r=mid;
}
print(r);
}
}
}