Codeforces Round #739 (Div. 3) 题解(A-F)
A. Dislike of Threes
题目大意:
输出第 k k k个既不被 3 3 3整除,尾数也不是 3 3 3的正整数。
解题思路:
因为 1 ≤ k ≤ 1000 1\le k\le 1000 1≤k≤1000,所以直接枚举就行了。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int a[N];
bool check(int x){
if(x%3==0||x%10==3) return false;
return true;
}
int main()
{
int cnt=0;
for(int i=1;cnt<=1000;i++)
if(check(i)) a[++cnt]=i;
int T;
scanf("%d",&T);
while(T--){
int k;
scanf("%d",&k);
printf("%d\n",a[k]);
}
}
B. Who’s Opposite?
题目大意:
n n n个数字按顺时针围成一个圈,每个数的对立面都会有一个相对的数。现在给出 a a a和跟 a a a相对的数 b b b,问 c c c的相对的数字是多少,如果不存在就输出 − 1 -1 −1。
解题思路:
通过观察题意可以发现,对于一个 n n n个数字围城的一个圈,相对的两个数之间的差是 n 2 \dfrac{n}2 2n。
所以我们可以根据 a a a和 b b b得到 n n n的大小,如果 a a a、 b b b、 c c c大于 n n n就输出 − 1 -1 −1。
因为 c c c和 d d d的差值一定是 n 2 \dfrac{n}2 2n,所以 d = c ± n 2 d=c\pm\dfrac{n}2 d=c±2n。
代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int T;
scanf("%d",&T);
while(T--){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
int n=abs(a-b)*2;
if(a>n||b>n||c>n) puts("-1");
else{
if(c+n/2<=n) printf("%d\n",c+n/2);
else printf("%d\n",c-n/2);
}
}
return 0;
}
C. Infinity Table
题目大意:
按照题意的规则填数,问第 k k k个数的坐标是多少。
解题思路:
观察之后可以发现,第 i i i轮填 2 × i − 1 2\times i-1 2×i−1个数,先从 ( 1 , i ) (1,i) (1,i)开始向下填 i i i个数,再向左填 i − 1 i-1 i−1个数。
所以可以通过累加和的形式找到第 k k k个数是第几轮填上去的,再根据剩余的步数判断坐标。
时间复杂度 O ( k ) O(\sqrt k) O(k)
代码:
#include<bits/stdc++.h>
using namespace std;
int n;
int main()
{
int T;
cin>>T;
while(T--){
cin>>n;
int sum=0,p;
for(int i=1;;i++){
if(sum+2*i-1>=n){
p=i;
break;
}
sum+=2*i-1;
}
n-=sum;
if(n<=p) printf("%d %d\n",n,p);
else printf("%d %d\n",p,2*p-n);
}
return 0;
}
D. Make a Power of Two
题目大意:
对于给出的整数 n n n,可以采取以下两种操作:
- 删除任何一位上的数字
- 从右边添加一位数字
执行操作的过程不允许出现前导零。
问从 n n n变成任何一个 2 2 2的整次幂需要的最少操作步数。
解题思路:
因为 1 ≤ n ≤ 1 0 9 1\le n \le10^9 1≤n≤109,所以我们只用考虑 [ 1 , 1 0 18 ] [1,10^{18}] [1,1018]内所有的 2 2 2的整次幂,一共只有 60 60 60个。
所以我们只用计算 n n n转换成将这所有的 2 2 2的整次幂所需的操作步数,取一个最小值就行了。
假设我们要转变的 2 2 2的整次幂是 a a a,我们将 n n n和 a a a都当做字符串来处理,其中 n n n中能留下的字符数量就是 n n n中子序列与 a a a前缀匹配的最大长度,设这个最大长度为 l e n len len,那么我们所需的操作步骤就是将 n . s i z e ( ) − l e n n.size()-len n.size()−len个字符删除,并从右边加上 a . s i z e ( ) − l e n a.size()-len a.size()−len个字符。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=100;
LL a[N];
void init()
{
a[0]=1;
for(int i=1;i<60;i++) a[i]=a[i-1]*2;
}
int check(LL x,LL y){
string s1=to_string(x),s2=to_string(y);
int i=0,j=0;
while(i<s1.size()&&j<s2.size()){
if(s1[i]==s2[j]) i++,j++;
else i++;
}
return s1.size()-j+s2.size()-j;
}
int main()
{
init();
int T;
scanf("%d",&T);
while(T--){
int n;
cin>>n;
int res=1e9;
for(int i=0;i<60;i++) res=min(res,check(n,a[i]));
printf("%d\n",res);
}
return 0;
}
E. Polycarp and String Transformation
题目大意:
对于一个字符串 s s s,一直执行以下操作直到字符串为空,字符串 t t t初始是个空字符串:
- 将字符串 s s s拼接到字符串 t t t的后面,即 t = t + s t=t+s t=t+s。
- 选择一个 s s s中存在的字符 c c c,删除字符串 s s s中所有的c
现在给出字符串 t t t,求原始字符串和删除字符的顺序,如果不存在输出 − 1 -1 −1。
解题思路:
因为字符串中的字符种类是越来越少的,所以我们可以直接反着遍历一遍字符串 t t t,字符出现的顺序就是删除字符顺序的逆序。
除此之外,我们还可以发现一个性质。
如果一个字符是第一次删除的,这个字符在 t t t中出现的次数 = s =s =s中出现的次数。
如果一个字符是第二次删除的,这个字符在 t t t中出现的次数$ = s 中 出 现 的 次 数 中出现的次数 中出现的次数\times 2$。
. . . . . . ...... ......
同理,我们就可以通过字符在 t t t中出现的次数以及删除顺序就可以得到原字符串 s s s的长度并进一步得到原字符串 s s s。
这样我们只需要将现在得到的字符串 s s s按照删除序列执行题意中的操作,看得到的结果与 t t t是否一致,一致的话就输出 s s s和 o r d e r order order,否则输出 − 1 -1 −1。
代码:
#include<bits/stdc++.h>
using namespace std;
string t;
int num[26];
string get(string t){
string res;
for(int i=t.size()-1;i>=0;i--){
if(!num[t[i]-'a']) res.push_back(t[i]);
num[t[i]-'a']++;
}
reverse(res.begin(),res.end());
return res;
}
string check(string s,string order){
string res=s;
for(int i=0;i<order.size();i++){
string temp;
for(int j=0;j<s.size();j++)
if(order[i]!=s[j]) temp+=s[j];
res+=temp;
s=temp;
}
return res;
}
int main()
{
int T;
cin>>T;
while(T--){
cin>>t;
memset(num,0,sizeof num);
string order=get(t);
int len=0;
for(int i=0;i<order.size();i++){
len+=num[order[i]-'a']/(i+1);
}
string s=s.substr(0,len);
if(check(s,order)==t) cout<<s<<" "<<order<<endl;
else cout<<-1<<endl;
}
return 0;
}
F. Nearest Beautiful Number (hard version)
题目大意:
给出两个整数 n n n和 k k k,输出不小于 n n n的最小k-beautiful数。
k-beautiful数的含义是最多由 k k k个不同的数字构成的整数。
解题思路:
难易版本的区别在于 k k k的范围,简单版本的 1 ≤ k ≤ 2 1\le k\le 2 1≤k≤2,因为 k k k比较小,可以直接通过一层循环或者两层循环枚举所有的情况,这里就不再赘述了。
困难版本 k k k的数据范围是 1 ≤ k ≤ 10 1\le k \le 10 1≤k≤10,做法的主要思想是贪心,通过让高位的数尽可能小达到整个数尽可能小的目的。
因为如果当前数不满足限制的话,我们就要尝试找一个更大的数,那就意味着必须要有一位比原来大,那么我们就要贪心思考这个问题,每次找到一个尽可能低的位+1,再将后面的数全部置为零,第一次满足条件的时候就是最优解。
有一个要注意的细节,最优解一定是不用进位的,因为就算当前所有位上都填 9 9 9也会比进一位的情况更优,所以要注意别对 9 9 9进行 + 1 +1 +1。
代码:
#include<bits/stdc++.h>
using namespace std;
int check(string n){
set<char> se;
for(auto c:n) se.insert(c);
return se.size();
}
int main()
{
int T;
cin>>T;
while(T--){
string n;
int k;
cin>>n>>k;
while(check(n)>k){
set<char> se;
for(int i=0;i<n.size();i++){
se.insert(n[i]);
if(se.size()>k){
while(n[i]=='9') i--;
n[i]++;
for(int j=i+1;j<n.size();j++) n[j]='0';
break;
}
}
}
cout<<n<<endl;
}
return 0;
}