K - Number with Bachelors 数位dp + 思维
题目大意:
定义一种数 Y:表示把Y分解,分解出来的每一位没有重复的,例如:101就不是Y这种数,123是Y这种数
有n次查询,有两种查询操作:
- 0 后面给两个数a,b,求a到b之间的Y这种数
- 1 后面给定x,求第x大的Y数
有两种不同进制的询问,十进制和16进制
题解:
很明显第一个种操作就是一个数位dp,第二种操作想清楚可以发现是一个二分+数位dp
这个题目一定要注意进制之间的转化,建议写一个输入函数和输出函数,这样就可以统一处理10进制和16进制了。
最后还要注意的是按照正常的数位dp(也就是我的这种写法) d 1 1这个样例会TLE,具体原因可以输入一下二分的mid就可以发现了,所以要把 1 这种操作的 x == 1特判掉,还有就是注意0这种操作的a可能是0,所以也要判断一下。
细节比较多。
#include <bits/stdc++.h>
using namespace std;
const int maxn = (1<<16) + 10;
typedef unsigned long long ull;
ull dp[2][22][maxn];
int type,a[100];
ull dfs(int pos,int sta,int limit){
if(pos==-1) return 1;
int ty = (type==10)?0:1;
if(dp[ty][pos][sta]!=-1&&!limit) return dp[ty][pos][sta];
int up = limit?a[pos]:type - 1;
ull ans = 0;
for(int i=0;i<=up;i++){
if((sta>>i)&1) continue;
if(sta==0&&i==0) ans+=dfs(pos-1,sta,limit&&i==up);
else ans+=dfs(pos-1,sta|(1<<i),limit&&i==up);
}
if(!limit) dp[ty][pos][sta] = ans;
// printf("pos = %d sta = %d limit = %d ans = %llu\n", pos,sta,limit,ans);
return ans;
}
ull solve(ull x){
int pos = 0;
while(x){
a[pos++] = x%type;
x/=type;
}
return dfs(pos-1,0,true);
}
void input(ull &x){
x = 0;
char s[22];
scanf("%s",s+1);
int len = strlen(s+1);
for(int i=1;i<=len;i++) {
if(s[i]<='9'&&s[i]>='0') x = x * type + s[i] - '0';
else x = x * type + s[i] - 'a' + 10;
}
}
void print(ull x){
if(x==0){
printf("0\n");
return ;
}
if(type == 10) printf("%llu\n", x);
else{
vector<int>ans;
while(x){
int p = x % type;
x/=type;
ans.push_back(p);
}
for(int i=ans.size()-1;i>=0;i--) {
if(ans[i]>=10) printf("%c", ans[i] - 10 + 'a');
else printf("%c", ans[i]+'0');
}
printf("\n");
}
}
void test(){
int t;
scanf("%d",&t);
while(t--){
int x;
scanf("%d%d",&x,&type);
printf("%llu",solve(x));
}
}
int main(){
memset(dp,-1,sizeof(dp));
// test();
int T;
scanf("%d",&T);
while(T--){
char op[10];
scanf("%s",op);
if(op[0]=='d') type = 10;
else type = 16;
int flag;
scanf("%d",&flag);
if(!flag){
ull a,b;
input(a),input(b);
// printf("ss a = %llu b = %llu\n", a,b);
ull ans = solve(b);
if(a>0) ans-=solve(a - 1);
print(ans);
}
else{
ull x;
input(x);
if(x<10){
printf("%llu\n", x - 1);
continue;
}
ull l = 0,r = 0,ans = 0;r--;
if(solve(r)<x){
printf("-\n");
continue;
}
while(l<=r){
ull mid = l + (r - l) / 2;
if(solve(mid)>=x) r = mid - 1,ans = mid;
else l = mid + 1;
}
print(ans);
}
}
}