1811 E Living Sequence 两种解法
Posted Edwin_Aze
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了1811 E Living Sequence 两种解法相关的知识,希望对你有一定的参考价值。
思维 进制转换 数位DP 无前导0 T3
Problem - 1811E - Codeforces
题目大意
从一个不含有数字4的递增序列中找第k个数并输出。
如 \\(1,2,3,5,6,7,8,9,10,11,12\\), \\(k = 4\\) 时输出 \\(5\\)。
思路1
有一个巧妙的解法:
考虑这个问题, 从一个没有限制的从1开始的递增序列找出第k个数, 显然就是十进制的k。而这里则可以定义新的进制为 "012356789" 9进制, 那么k对应的就是这个特殊的九进制数, 我们只需要把它转换为十进制就行。
二转十:
while(k)
ans += k % 2, k /= 2;
九转十:
while(k)
ans += k % 9, k /= 9;
代码1
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
using LL = long long;
int a[20];
int cnt = 0;
int main()
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
string s = "012356789";
int T;
cin >> T;
while (T--)
LL k;
cin >> k;
cnt = 0;
while (k)
a[cnt++] = s[k % 9] - \'0\', k /= 9;
for (int i = cnt - 1; i >= 0; i--)
cout << a[i];
cout << endl;
思路2
也可以考虑数位DP, 定义 \\(f(i,j)\\) 为长度为i, 且最高位为j的数, 可以写出这样的初始化函数来得到 \\([1,i]\\) 的满足条件的数的个数:
void init()
for (int i = 0; i <= 9; i++)
if (i != 4)
f[1][i] = 1;
for (int i = 2; i <= N - 1; i++)
for (int j = 0; j <= 9; j++)
if (j == 4)
continue;
for (int k = 0; k <= 9; k++)
f[i][j] += f[i - 1][k];
然后再实现查找前缀和 \\([1,num]\\) 的满足条件的数的个数, 题目中的 \\(k\\) 最大为 1e12, 直接二分结果, 找最左边且 \\(dp(mid) = k\\) 的值就是最终结果。
记得要处理前导0, 方法是在首尾不加上0开头的部分, 最后再加一遍所有长度小于 num.size() 的部分。
代码2
#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
#include <vector>
using namespace std;
const int N = 17;
typedef long long ll;
const ll INF = 1e17;
ll f[N][10];
void init()
for (int i = 0; i <= 9; i++)
if (i != 4)
f[1][i] = 1;
for (int i = 2; i <= N - 1; i++)
for (int j = 0; j <= 9; j++)
if (j == 4)
continue;
for (int k = 0; k <= 9; k++)
f[i][j] += f[i - 1][k];
ll dp(ll x)
if (!x)
return 0;
vector<int> nums;
while (x)
nums.push_back(x % 10), x /= 10;
ll res = 0;
for (int i = nums.size() - 1; i >= 0; i--)
int x = nums[i];
for (int j = (i == nums.size() - 1); j < x; j++)
res += f[i + 1][j];
if (x == 4)
break;
if (!i)
res++;
for (int i = 1; i <= nums.size() - 1; i++)
for (int j = 1; j <= 9; j++)
res += f[i][j];
return res;
int main()
init();
int T;
cin >> T;
while (T--)
ll k;
cin >> k;
ll l = -1, r = 1e13;
while (l != r - 1)
ll mid = l + r >> 1;
if (dp(mid) < k)
l = mid;
else
r = mid;
cout << r << endl;
return 0;
E - Number Sequence
Description
Input
Output
Sample Input
2 8 3
Sample Output
2 2
这道题不打表会超时 ,以下 超时代码
#include<iostream> #include<cmath> using namespace std; void f(int n) { int p=1,j=1,t; bool flag; while(1){ flag=false; for(j=0;j<=p;j++){ int i; for(i=0;;i++){ t=pow((double)10,i); if(j/t==0){ n-=i; break; } } if(n<=0){ int k=i+n; flag=true; n=j%(int)(pow((double)10,i-k+1))/pow((double)10,i-k); break; } } if(flag==true)break; p++; } cout<<n<<endl; } int main() { int t; cin>>t; while(t--){ int n; cin>>n; f(n); } //system("pause"); return 0; }
打表后!!!这个代码不好理解
#include<iostream> #include<cmath> using namespace std; unsigned int a[31270],s[31270]; void f() { int i; a[1]=1; s[1]=1; for(i=2;i<31270;i++) { a[i]=a[i-1]+(int)log10((double)i)+1; //记录1至s[i]个数字的位数和 s[i]=s[i-1]+a[i]; //一位 记录 1至s[i]个数字 } } int main() { int t; int n; int i; cin>>t; f(); while(t--) { cin>>n; i=1; while(s[i]<n) i++; int pos=n-s[i-1]; int tmp=0; for(i=1;tmp<pos;i++) //第n个数字在s[i-1]这个数据组中 { tmp+=(int)log10((double)i)+1; } int k=tmp-pos; //数字i从低位数的第k+1位 cout<<(i-1)/(int)pow(10.0,k)%10<<endl; } return 0; }
以上是关于1811 E Living Sequence 两种解法的主要内容,如果未能解决你的问题,请参考以下文章
[洛谷P3929]SAC E#1 - 一道神题 Sequence1
论文阅读Research on video adversarial attack with long living cycle
[codeforces]#350E. Correct Bracket Sequence Editor