Codeforces Round #739 (Div. 3)
Posted Maystern
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #739 (Div. 3)相关的知识,希望对你有一定的参考价值。
Codeforces Round #739 (Div. 3)
A - Dislike of Threes
有\\(t(1\\leq t \\leq 100)\\)组数据,每组数据给出一个\\(k(1\\leq k \\leq 1000)\\),
求出第\\(k\\)小的正整数满足不被\\(3\\)整除,数的结尾不是\\(3\\)。
可以发现,按照上述方式构造的数,大致是线性分布,因此
考虑\\(O(k)\\)预处理,\\(O(1)\\)查询。
# include<bits/stdc++.h>
using namespace std;
bool check(int x){
if (x%3==0) return 0;
if (x%10==3) return 0;
return 1;
}
int main()
{
int now=1;
vector<int>a;
while (1) {
if (check(now)) a.push_back(now);
now++;
if (a.size()>1000) break;
}
int t; scanf("%d",&t);
while (t--) {
int x; scanf("%d",&x);
printf("%d\\n",a[x-1]);
}
return 0;
}
B - Who\'s Opposite?
有\\(t(1\\leq t \\leq 10^4)\\)组数据,每组数据给出\\(a,b,c (1\\leq a,b,c \\leq 10^8)\\),
考虑在一个由\\(1-n\\)顶点按照顶点编号顺时针排列的环上,\\(n\\)为偶数。
顶点\\(a\\)和顶点\\(b\\)构成的直线恰好是对称轴,若顶点\\(c\\)和顶点\\(d\\)构成的直线也恰好是对称轴。
求出顶点\\(d\\)的值,若输入数据错误,不存在\\(d\\),输出\\(-1\\)
考虑将所有顶点编号都减去\\(1\\),所以顶点编号是\\(0-(n-1)\\)这样可以更方便的使用整除的性质。
即\\(a\' = a-1,b\'=b-1,c\'=c-1,d\'=d-1\\)
显然,利用\\(a\',b\'\\)可以求出\\(n = 2\\times |a\'-b\'|\\).
而$c\'-d\' ≡ a\'-b\' \\pmod{n} $
因此\\(d\' = c\'-a\'+b\' \\pmod{n}\\) 所以 \\(d = d\'+1\\)
不符合的条件输出\\(-1\\),就是 \\(a\',b\',c\'\\)中存在一个,大于等于\\(n\\),说明输入错误。
# include<bits/stdc++.h>
using namespace std;
int solve(int a,int b,int c) {
int n=abs(a-b)*2;
if (a>=n||b>=n||c>=n) return -1;
return (((c-a)%n+n)%n+b)%n+1;
}
int main()
{
int t; scanf("%d",&t);
while (t--) {
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
a--;b--;c--;
int ans = solve(a,b,c);
printf("%d\\n",ans);
}
return 0;
}
C - Infinity Table
有\\(t(1\\leq t \\leq 100)\\)组数据,每组数据给出一个\\(k(1\\leq k \\leq 10^9)\\),
求出值\\(k\\)在下列无限矩阵中的第几行第几列,输出行号和列号。
1 2 5 10 ... 4 3 6 11 ... 9 8 7 12 ... 16 15 14 13 ... ... ... ... ... ...
首先考虑第\\(1\\)列,发现是完全平方数,\\((i,1)\\)中存储的值是\\(i^2\\)且反\\(L\\)型的边长为\\(i\\)。
第\\(i\\)个反\\(L\\)型中包含的数字有\\([(i-1)^2+1,i^2]\\),因此\\(k\\)在第\\(\\lceil \\sqrt{k}\\rceil\\)个反\\(L\\)型中。
接下来考虑\\(k\\)在反\\(L\\)型中是行号为\\(\\lceil \\sqrt{k}\\rceil\\)还是列号为\\(\\lceil \\sqrt{k}\\rceil\\)。
观察\\((i,i)\\)上的数,其值为\\(i^2-i+1\\),因此
-
若\\(k \\leq \\lceil \\sqrt{k}\\rceil^2 -\\lceil \\sqrt{k}\\rceil + 1\\),则列号为\\(\\lceil \\sqrt{k}\\rceil\\)可以求出行号\\(k-(\\lceil \\sqrt{k}\\rceil -1)^2\\)
答案是\\((k-(\\lceil \\sqrt{k}\\rceil -1)^2,\\lceil \\sqrt{k}\\rceil)\\)
-
若\\(k \\geq \\lceil \\sqrt{k}\\rceil^2 -\\lceil \\sqrt{k}\\rceil + 1\\),则行号为\\(\\lceil \\sqrt{k}\\rceil\\)可以求出列号\\(\\lceil \\sqrt{k}\\rceil ^2 - k + 1\\)
答案是\\((\\lceil \\sqrt{k}\\rceil,\\lceil \\sqrt{k}\\rceil ^2 - k + 1)\\)
# include <bits/stdc++.h>
using namespace std;
void solve() {
int k; scanf("%d",&k);
int s = ceil(sqrt(k));
if (k<=s*s-s+1) {
printf("%d %d\\n",k-(s-1)*(s-1),s);
} else {
printf("%d %d\\n",s,s*s-k+1);
}
}
signed main()
{
int t; scanf("%d",&t);
while (t--) {
solve();
}
return 0;
}
D - Make a Power of Two
有\\(t(1\\leq t \\leq 10^4)\\)组数据,每组数据给出一个数\\(n(1\\leq n\\leq 10^9)\\),
求最小的操作次数,让\\(n\' = 2^k (k \\in N)\\)
- 删除数\\(n\\)中的一个数位。
- 在数\\(n\\)尾部添加任意一个数字。
在数据范围内,有\\(2^0,2^1,...,2^{62}\\)这\\(63\\)个\\(2\\)的幂次,
对于\\(n\\),变成每一个\\(2\\)的幂次都计算一遍最下操作次数,再取最小值,就是最终答案了。
下面考虑\\(n\\)和\\(s\\),求最小操作次数,让\\(n\\)变成\\(s\\)
首先应该在\\(n\\)中删除一些数字,删除最少的情况就是保留最多的情况。
即\\(n\\)中的子序列,和\\(s\\)中从\\(1\\)开始的连续子串相等并且长度最大的情况,记此长度为\\(r\\)。
那么在\\(n\\)中应该删除\\(|n| - r\\)个元素形成\\(n\'\\),在\\(n\'\\)中添加\\(|s|-r\\)个元素形成\\(s\\)。
所以,答案就是\\(|n|+|s| - 2r\\)
\\(r\\)的计算用双指针可以轻松解决。
时间复杂度为\\(O(63\\times t\\times (|n|+|s|))\\)
# include<bits/stdc++.h>
# define int long long
# define pow hhh
# define inf (1e9)
using namespace std;
int pow[105],a[105],b[105],f[105][105];
int work(int x,int y) {
a[0]=b[0]=0;
while (x) { a[++a[0]]=x%10; x/=10;}
while (y) { b[++b[0]]=y%10; y/=10;}
reverse(a+1,a+1+a[0]); reverse(b+1,b+1+b[0]);
int i=1,j=1;
while (i<=a[0]&&j<=b[0]) {
if (a[i]==b[j]) i++,j++;
else i++;
}
int res=j-1;
return a[0]+b[0]-2*res;
}
signed main() {
int t; scanf("%lld",&t);
pow[0]=1;
for (int i=1;i<63;i++) pow[i]=pow[i-1]*2;
while (t--) {
int n; scanf("%lld",&n);
int ans = 1e9;
for (int i=0;i<63;i++) {
ans=min(ans,work(n,pow[i]));
}
printf("%lld\\n",ans);
}
return 0;
}
E - Polycarp and String Transformation
有\\(T\\)组数据,\\(1 \\leq T \\leq 10^4\\)
初始有字符串\\(s\\),\\(t\\)为空。连续依次操作,\\(t +=s\\),\\(s\\)删除字符\\(c\\),前提是\\(c \\in s_i\\)直到\\(s = ""\\)
给出若干次操作后的结果\\(t\\),求出原字符串\\(s\\),和依次删除字符的顺序\\(c\\)
保证所有输入字符的总数不会超过\\(5\\times 10^5\\)
显然,\\(t\\)串倒序出现字母种类的顺序就是\\(s\\)串删除字母的顺序。
这是基于若当前删除一个字母,那么这个串后面的拼接到\\(t\\)的串上都不会出现这个字母了。
因此,很容易的就获得了,依次删除字符的顺序。
设\\(t\\)的长度为\\(n\\),有\\(m\\)种不同的字符,设\\(cnt[c]\\)表示字符\\(c\\)在\\(t\\)字符串中总共出现的次数,
那么初始的\\(s\\)经过且只经过\\(m\\)次操作,
基于每一次只删除一个字符的原理,我们可以计算出一个字符在原字符串\\(s\\)中出现的次数。
例如,最后一个被删除的字符\\(c_m\\),在原字符串\\(s\\)中出现的次数为\\(\\frac{cnt[c_m]}{m}\\)
倒数第\\(2\\)个被删除的字符\\(c_{m-1}\\),在原字符串\\(s\\)中出现的次数为\\(\\frac{cnt[c_{m-1}]}{m-1}\\)
因此\\(c_i (1\\leq i \\leq w)\\)在原字符串中出现的次数为\\(\\frac{cnt[c_i]}{i}\\)次,那么原字符串长度为\\(|s| = \\sum_{i=1}^{m} \\frac{cnt[c_i]}{i}\\)
这样,从\\(t[1]\\)开始往后长度为\\(|s|\\) 的字符串,是唯一有可能满足情况的答案。
返过去模拟验证,可以排除\\(-1\\)的情况,剩下的就是正确的答案。
时间复杂度\\(O(\\sum |t|)\\)
include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int cnt[26];
int used[26];
char ans[N];
int main() {
int t; scanf("%d",&t);
while (t--) {
string s; cin>>s;
int n=s.length(),tot=0;
for (int i=0;i<26;i++) {
cnt[i]=0; used[i]=0;
}
for (int i=n-1;i>=0;i--) {
if (!used[s[i]-\'a\']) used[s[i]-\'a\']=++tot;
cnt[s[i]-\'a\']++;
}
for (int i=0;i<26;i++) if (used[i]) {
ans[used[i]]=i+\'a\';
}
int res = 0;
for (int i=1;i<=tot;i++) res+=cnt[ans[i]-\'a\']/(tot-i+1);
reverse(ans+1,ans+1+tot);
string s1 = "",s2 = "";
for (int i=0;i<res;i++) s1+=s[i];
int now=1;
while (s1!="") {
s2+=s1;
string w="";
for (int i=0;i<s1.length();i++) if (ans[now]!=s1[i]) {
w+=s1[i];
}
s1=w;
now++;
}
if (s2 == s) {
for (int i=0;i<res;i++) printf("%c",s[i]);
putchar(\' \');
for (int i=1;i<=tot;i++) printf("%c",ans[i]);
puts("");
} else puts("-1");
}
return 0;
}
F - Nearest Beautiful Number
有\\(t(1\\leq t \\leq 10^4)\\)组数据,每组数据给出\\(2\\)个数\\(n,k (1\\leq n\\leq 10^9,1 \\leq k\\leq 10)\\),
求出最小的\\(x\\geq n\\),满足\\(x\\)由最多\\(k\\)种数字构成。
方法一:动态规划。
\\(f[i][mask][0/1]\\)表示当前从最高位到最低位依次访问,当前到低\\(i\\)位,取数集合为\\(mask\\),\\(0\\)当前数前缀和目标前缀相等,\\(1\\)当前数前缀已经大于目标前缀了。
\\(g[i][mask][0/1]\\)表示当前从最高位到最低位依次访问,当前到低\\(i\\)位,取数集合为\\(mask\\),\\(0\\)当前数前缀和目标前缀相等,\\(1\\)当前数前缀已经大于目标前缀了。此时的最小前缀值。
初始值:\\(f[0][0][0] = 1 ,g[0][0][0] = 0\\) otherwise : \\(f[i][mask][0/1]=0,g[i][mask][0/1]=inf\\)
转移:考虑\\(i\\)向\\(i+1\\)转移。
- $f[i+1][mask|(1<<a[i+1])][0]=1; $ (条件:\\(f[i][mask][0] =1\\))同时更新\\(g\\)
- $f[i+1][mask|(1<<j)][1]=1; $ (条件:\\(f[i][mask][0] =1\\)并且\\(a[i+1]+1\\leq j\\leq 9\\))同时更新\\(g\\)
- \\(f[i+1][mask|(1<<j)][1]=1;\\) (条件:\\(f[i][mask][1] =1 , 0 \\leq j \\leq 9\\))同时更新\\(g\\)
答案为 \\(\\min \\limits_{1\\leq count(mask)\\leq k }\\ g[n][mask][0/1]\\)
时间复杂度为\\(O(t\\times 2^k\\times k\\times |n|)\\) 数量级为\\(10^9\\),会\\(TLE\\)
# include <bits/stdc++.h>
# define int long long
# define inf (1e18)
using namespace std;
bool f[10][1024][2];
int g[10][1024][2],a[15];
char s[15];
inline int read()
{
int x=0;char c=getchar();
while (c<\'0\'||c>\'9\') c=getchar();
while (c>=\'0\'&&c<=\'9\') x=x*10+c-\'0\',c=getchar();
return x;
}
void write(int x) {
if (x>9) write(x/10);
putchar(x%10+\'0\');
}
int count(int x) {
int res=0;
for (;x;x-=x&(-x)) res++;
return res;
}
signed main() {
int t=read();
while (t--) {
scanf("%s",s+1); int k=read(),n=strlen(s+1);
for (int i=1;i<=n;i++) a[i]=s[i]-\'0\';
for (int i=1;i<=n;i++)
for (int mask=0;mask<(1<<10);mask++)
for (int j=0;j<=1;j++) {
f[i][mask][j]=0;
g[i][mask][j]=inf;
}
f[0][0][0]=1; g[0][0][0]=0;
for (int i=0;i<n;i++)
for (int mask=0;mask<(1<<10);mask++) {
if (count(mask)>k) continue;
if (f[i][mask][0]) {
f[i+1][mask|(1<<a[i+1])][0]=1;
g[i+1][mask|(1<<a[i+1])][0]=min(g[i+1][mask|(1<<a[i+1])][0],g[i][mask][0]*10+a[i+1]);
for (int j=a[i+1]+1;j<10;j++) {
f[i+1][mask|(1<<j)][1]=1;
g[i+1][mask|(1<<j)][1]=min(g[i+1][mask|(1<<j)][1],g[i][mask][0]*10+j);
}
}
if (f[i][mask][1]) {
for (int j=0;j<10;j++) {
f[i+1][mask|(1<<j)][1]=1;
g[i+1][mask|(1<<j)][1]=min(g[i+1][mask|(1<<j)][1],g[i][mask][1]*10+j);
}
}
}
int ans = inf;
for (int mask=0;mask<(1<<10);mask++) {
if (count(mask)>k) continue;
if (f[n][mask][0]) ans=min(ans,g[n][mask][0]);
if (f[n][mask][1]) ans=min(ans,g[n][mask][1]);
}
write(ans); putchar(\'\\n\');
}
return 0;
}
方法二:贪心。
考虑\\(O(2^k)\\)枚举,需要的元素总数,然后尝试用这些元素去填充大于等于\\(n\\)的\\(x\\),然后取最小的\\(x\\)。
若当前选择的数可以和\\(n\\)的前缀匹配,那么就按照\\(n\\)的前缀填写\\(x\\)的对应值,
否则,
- 若当前位存在至少一个比\\(n\\)的当前位要大的数字,那么选取这些数字里最小的那个填充,剩余的用需要选择的元素中最小值填充。
- 若当前位不存在至少一个比\\(n\\)的当前位要大的数字,那么选取最近的一位存在至少一个比\\(n\\)的对应位要大的数字,将该位改为这些数字里最小的那个填充,剩余的用需要选择的元素中最小值填充。
时间复杂度\\(O(t\\times 2^k\\times |n|)\\) 数量级为\\(10^8\\),不一定\\(TLE\\)
# include <bits/stdc++.h>
# define int long long
# define inf (1e18)
# define lowbit(x) (x&(-x))
using namespace std;
int a[10];
int count(int x) {
int res=0;
for (;x;x-=lowbit(x)) res++;
return res;
}
signed main()
{
int t; scanf("%lld",&t);
while (t--) {
int n,k; scanf("%lld%lld",&n,&k); a[0]=0;
while (n) { a[++a[0]]=n%10; n/=10;}
reverse(a+1,a+1+a[0]);
int ans = inf;
for (int mask=1;mask<(1<<10);mask++)
if (count(mask)<=k) {
int res=0;
for (int i=1;i<=a[0];i++) {
if (mask&(1<<a[i])) res=res*10+a[i];
else {
int pos=-1,mn,mx;
for (int j=a[i]+1;j<10;j++) if (mask&(1<<j)) {
pos=j; break;
}
for (int j=0;j<10;j++) if (mask&(1<<j)) {
mn=j; break;
}
for (int j=9;j>=0;j--) if (mask&(1<<j)) {
mx=j; break;
}
if (pos==-1) {
int w=-1;
for (int j=i;j>=1;j--) if (a[j]<mx) {
w=j; break;
}
if (w==-1) {
res=inf; break;
}
int nn;
for (int j=a[w]+1;j<10;j++) if (mask&(1<<j)) {
nn=j; break;
}
res=0;
for (int j=1;j<=w-1;j++) res=res*10+a[j];
res=res*10+nn;
for (int j=w+1;j<=a[0];j++) res=res*10+mn;
} else {
res=res*10+pos;
for (int j=i+1;j<=a[0];j++) res=res*10+mn;
}
break;
}
}
ans=min(ans,res);
}
printf("%lld\\n",ans);
}
return 0;
}
以上是关于Codeforces Round #739 (Div. 3)的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces Round #739 (Div. 3) 题解
Codeforces Round #739 (Div. 3)A-F2
Codeforces Round #739 (Div. 3) ABCDEF1F2题解
Codeforces Round #739 (Div. 3) ABCDEF1F2题解
Codeforces Round #739 (Div. 3)(补题)
Codeforces Round #739 (Div. 3) F1. Nearest Beautiful Number (easy version)