Codeforces Round #739 (Div. 3)
A. Dislike of Threes
题目大意:输出第\(k\)个不能被\(3\)整除且个位数不是\(3\)的数。
分析:暴力枚举,从\(1\)开始,判断每个数\(x\)是否\(x\%3\neq 0 且 x\%10\neq 3\)
代码:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
void read(int &sum)
{
sum=0;char last='w',ch=getchar();
while (ch<'0' || ch>'9') last=ch,ch=getchar();
while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
if (last=='-') sum=-sum;
}
int main()
{
// freopen("M.in","r",stdin);
// freopen("M.out","w",stdout);
int len=0,a[10000],i=1;
while (len<1000)
{
if (i%3!=0 && i%10!=3) a[++len]=i;
i++;
}
int t;
read(t);
while (t--)
{
int x;read(x);
printf("%d\n",a[x]);
}
// fclose(stdin);fclose(stdout);
return 0;
}
B. Who's Opposite?
题目大意:有\(n(2|n)\)个人围成一个圈,每个人编号分别为\(1,2,3...n\),第\(x\)个人正对面的人是第\(y\)个人,给出\(x,y\),问第\(z\)个人所对的人的编号是多少,无解输出\(-1\)。
分析:
- 观察两个人何时能相对,设\(s_1\)为\(x,y\)左边间隔的人,设\(s_2\)为\(x,y\)右边间隔的人,发现
当且仅当\(s_1=s_2\)时,\(x,y\)是相当的
- 所以令\(a=min(x,y),b=max(x,y)\),那么\(s_2=y-x-1\),\(s_1=s_2\),但明显\(s_1\)至少为\(1\)~\(x-1\)的人数,所以\(s_1\geqslant x-1\),如果\(s_2<x-1\)那么就是无解
- 通过\(s_1\),\(s_2\),容易求出\(n=s_1+s_2+2\),如果\(z>n\)也是无解
- 最后根据\(s_2\)可以得出于\(z\)相对的人的位置在\((z+s_2)\)的位置上,\(\%\)一下就好了
代码:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
void read(int &sum)
{
sum=0;char last='w',ch=getchar();
while (ch<'0' || ch>'9') last=ch,ch=getchar();
while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
if (last=='-') sum=-sum;
}
int t;
int a,b,c;
int main()
{
// freopen("M.in","r",stdin);
// freopen("M.out","w",stdout);
read(t);
while (t--)
{
read(a),read(b),read(c);
if (a>b) swap(a,b);
if (a-1>b-a-1) { printf("-1\n"); continue; }
int n=b+((b-a-1)-(a-1));
if (c>n) { printf("-1\n"); continue; }
if ((b+c-a+n*4)%n==0) printf("%d\n",n);
else printf("%d\n",(b+c-a+n*4)%n);
}
// fclose(stdin);fclose(stdout);
return 0;
}
C. Infinity Table
题目大意:按图的方式填格子,问\(x\)在那个格子?

分析:
- 发现填充的顺序是
的一个顺序
- 所以把其分成\(n\)段向下,和向左移动的子段,发现\((n,1)=k^2\),所以可以知道\((\sqrt x)^2=(x的上一个平方数)\),于是就得到了\(x\)是从\((\sqrt x,1)\)开始往下的\(x-\sqrt x ^2\)的数所填充的位置
代码:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
void read(int &sum)
{
sum=0;char last='w',ch=getchar();
while (ch<'0' || ch>'9') last=ch,ch=getchar();
while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
if (last=='-') sum=-sum;
}
int t,n;
int main()
{
// freopen("M.in","r",stdin);
// freopen("M.out","w",stdout);
read(t);
while (t--)
{
read(n);
int k=sqrt(n)+1;
if (n==(k-1)*(k-1)) k--;
if (n-(k-1)*(k-1)<k) printf("%d %d\n",n-(k-1)*(k-1),k);
else printf("%d %d\n",k,k-(n-(k-1)*(k-1)-k));
}
// fclose(stdin);fclose(stdout);
return 0;
}
D. Make a Power of Two
题目大意:给出\(x\),每次可以删除\(x\)的任意一位,或在\(x\)末尾添一个数字,问使得\(x=2^k(k\geqslant 0)\)的最少次数
分析:
- 考虑极端情况,\(x\)有\(9\)位,删\(9\)位在加上\(1\)位\(2\),那\(x\)必然合法,所以最多操作\(10\)次,所以\(x\leqslant 10^{19}\),所以枚举\(k=1到63\),选取最小操作方案
- 这样,问题就变为了给两个数\(a,b\),问是的\(a\),变成\(b\)的最小操作次数,举个例子
\(1052\)变成\(1024\),因为我们是可以在末尾添加,而不能在首位添加,所以要使得高位相等的代价要高于使低位相等的代价,所以就要尽量使高位相等,从高位开始匹配即可,余下的位数要剪掉
代码:
在这里插入代码片#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
typedef long long ll;
void read(int &sum)
{
sum=0;char last='w',ch=getchar();
while (ch<'0' || ch>'9') last=ch,ch=getchar();
while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
if (last=='-') sum=-sum;
}
int t;
string n;
int sortm(int a,int b)
{
if (b==0) return 1;
if (b==1) return a;
int t=sortm(a,b/2);
if (b%2==1) return t*t*b;
else return t*t;
}
int solve(string a,int m)
{
string b;char f[100];int len=0;
while (m!=0)
{
char ch=m%10+'0';m/=10;
f[len]=ch;len++;
}
for (int i=len-1;i>=0;i--)
b+=f[i];
// cout << b << " " << a << endl;
int l=0,r=0;
int sum=0;
// printf("l,r : %d %d\n",l,r);
while (l<a.length() && r<b.length())
{
// printf("%c %c\n",a[l],b[r]);
if (a[l]==b[r]) l++,r++;
else l++,sum++;
}
// cout << a << " " << b << endl;
if (l!=a.length()) sum+=a.length()-1-l+1;
if (r!=b.length()) sum+=b.length()-1-r+1;
return sum;
}
signed main()
{
// freopen("M.in","r",stdin);
// freopen("M.out","w",stdout);
read(t);
while (t--)
{
cin >> n;
int k=1,ans=1<<30;
for (int i=0;i<=60;i++)
{
ans=min(ans,solve(n,k));
// printf("%lld %lld\n",solve(n,k),k);
k*=2;
}
printf("%lld\n",ans);
}
// fclose(stdin);fclose(stdout);
return 0;
}
题目大意:原来有一串字符\(s\),每次删除其中一种字母,直到为空串,设每次删除后的字符串为\(s_1,s_2...s_k\),则字符串\(t=s+s_1+s_2+...+s_k\),现在给出\(t\),问原字符串\(s\)是啥,以及字母删除的顺序。
分析:
- 首先可以发现,如果在一个位置\(mid\)上,\(1\) ~ \(mid\)中字符\(G\)出现了,但在\(mid+1\) ~ \(t.length()\)中没有出现,则可以断定\(G\)被删除了,从开头枚举到结尾,就可以得到删除的顺序了
- 那么如何求出\(s\)呢?观察\(t\)的构成,\(t=s+s_1+s_2+...+s_k\),那么根据每个字母删除的顺序,我们可以得到,在\(t\)中,每个字母出现了\(S_{在s中出现的次数}*T_{第几个删除的}\),因为\(s\)是\(t\)的开头,枚举结尾在哪,利用前缀和判断是否合法即可
代码:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int bz[26];
int* cnt=bz-'a';
pair<string,string> dep(string s)
{
string ch;
reverse(s.begin(), s.end());
for (auto c:s)
{
if (!cnt[c])
ch.push_back(c);
cnt[c]++;
}
int m = ch.length();
int yyds = 0;
for (int i = 0; i < m; i++)
yyds += cnt[ch[i]] / (m - i);
reverse(ch.begin(), ch.end());
return { string(s.rbegin(), s.rbegin() + yyds), ch };
}
string solve(pair<string, string> p)
{
string result = p.first;
for (auto c : p.second)
{
string temp;
for (auto d : p.first)
if (d != c)
{
temp.push_back(d);
result.push_back(d);
}
p.first = temp;
}
return result;
}
int t;
int main()
{
cin >> t;
while (t--)
{
memset(bz,0,sizeof(bz));
string s;
cin >> s;
auto ans=dep(s);
auto check=solve(ans);
if (check==s)
cout << ans.first << ' ' << ans.second << '\n';
else
cout << "-1\n";
}
return 0;
}
F. Nearest Beautiful Number
题目大意:给出\(n\),求大于等于\(n\)且最多有\(k\)个不同数字的最小数
分析:
- 从高位开始考虑,设当为第\(i\)位,值为\(x\),记录答案数字第\(i\)位的数组为\(a\),分两种情况
- 如何\(a\)不足\(k\)位,直接\(a[i]=x\)
- 如果\(a\)已经有\(k\)位了,两种情况
- 如果当位的值\(x\)已经在\(a\)中出现过了,直接填\(a[i]=x\)即可
- 如果当前位的值\(x\)没有在\(a\)中出现,也分两种情况
- 如果前面的值是大于\(n\)的前\(i-1\)位的,那么就可以填一个前面最小的数
- 如果前面的值是等于\(n\)的前\(i-1\)位的,分类
- 尝试修改从前面已经出现过的数字中,选一个\(\geqslant x\)且最小的数
- 在第一种失败的情况下,就考虑把\(a[1]\)$a[i-1]$中的某个数字变大,然后这一位在填$a[1]$\(a[i-1]\)中最小的数
代码
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
string solve()
{
string n;
int k;
cin >> n >> k;
while (true)
{
set<char> s;
for (auto c : n) s.insert(c);
if (s.size() <= k) return n;
s.clear();
int ptr = 0;
while (ptr++)
{
s.insert(n[ptr]);
if (s.size()>k)
{
while (n[ptr]=='9') ptr--;
n[ptr]++;
for (int i=ptr+1;i<n.size();i++)
n[i]='0';
break;
}
}
}
}
int t;
int main()
{
cin >> t;
while (t--)
cout << solve() << '\n';
return 0;
}