Codeforces Round #739 (Div. 3)
https://codeforces.com/contest/1560
A Dislike of Threes(💧水题)
去掉所有数中个位含有3的还有能被三整除的,放入数组中,输出给定顺序的某个位置的数字
#include<iostream>
#include<algorithm>
#include<queue>
typedef long long LL;
using namespace std;
const int N=3000;
int a[N],cnt=1;
bool check(int n)
{
if (n%10==3) return true;
return false;
}
void init(int n){
for(int i=1;i<=n;i++)
{
if(i%3==0||check(i)) continue;
//cout<<i<<" "<<cnt<<endl;
a[cnt++]=i;
}
}
int main()
{
init(3000);
int t;
cin>>t;
while (t--)
{
int k;
cin>>k;
cout<<a[k]<<endl;
}
return 0;
}
B Who’s Opposite?(找规律)
#include<iostream>
#include<cmath>
using namespace std;
int main()
{
int t;
cin>>t;
while (t--)
{
int a,b,c;
cin>>a>>b>>c;
int d=abs(a-b);
if(a>d*2||b>d*2||c>d*2){
puts("-1");
continue;
}
if(c<=d) cout<<c+d<<endl;
else if(c>d) cout<<c-d<<endl;
}
}
C Infinity Table(矩阵填数(找规律))
![img](https://espresso.codeforces.com/aa1eece2e042a16fcbc09f2af100e73049fd8841.png)
题意:
给出一个数字T
,输出它所在的位置坐标(i,j)
思路:
-
每行的第一个数字等于它所在的行数的平方( 1 2 , 2 2 , 3 2 ⋅ ⋅ ⋅ ⋅ 1^2,2^2,3^2···· 12,22,32⋅⋅⋅⋅)
-
以图示形式填数,每一部分的数字范围为 ( i − 1 ) 2 ~ i 2 (i-1)^2~i^2 (i−1)2~i2
思路1:将每一行的第一个数字存入数组中(从1开始存)
思路2:二分查找找到数字所在的部分的左右边界l,r
二分查找数组中小于等于数字T
数字的下标为l
,这就是这个数字所在的“部分”的左边界,右边界怎么求?右边界r
等于上一行的第一个数字+1,所以让r=l-1(上一行第一个数的下标,在数组中l的前一个位置)
- 观察任意一部分,一定有奇数个数字,中间位置的数字行数=列数=i(
(
1
,
1
)
,
(
2
,
2
)
,
(
3
,
3
)
⋅
⋅
⋅
⋅
(1,1),(2,2),(3,3)····
(1,1),(2,2),(3,3)⋅⋅⋅⋅),对于每一部分中的任意一个数
x
, ( i − 1 ) 2 − 1 < x < i 2 (i-1)^2-1<x<i^2 (i−1)2−1<x<i2,中间数字等于每一部分两端数字除2(中位数),比如第四部分的中位数为13=(10+16)/2,下标为(4,4)
。
思路3:找到这一部分的中间数字mid=(f[l]+f[r]+1)>>1(中位数)
-
如果数字
x<mid
,列数不变,行数不断-1,所以 i x = i m i d − ( m i d − x ) i_{x}=i_{mid}-(mid-x) ix=imid−(mid−x)如果数字
x>mid
,行数不变,列数不断-1,所以 j x = j m i d − ( x − m i d ) j_x=j_{mid}-(x-mid) jx=jmid−(x−mid)
思路4:利用数字x
与这一部分中位数的差值计算(i,j)
代码:
#include <bits/stdc++.h>
using namespace std;
int t, x;
vector<int> f;
void init()
{
f.push_back(0);
for (int i = 1; i < 0x3f3f3f3f / i; i++)
f.push_back(i * i);
}
int main()
{
init();
scanf("%d", &t);
while (t --)
{
scanf("%d", &x);
int r = lower_bound(f.begin(), f.end(), x) - f.begin(), l = r - 1;
int mid = (f[l] + f[r] + 1) >> 1, a = r, b = r;
if (mid > x) a -= mid - x;
else b -= x - mid;
printf("%d %d\n", a, b);
}
return 0;
}
D Make a Power of Two(思维+贪心)
题意:
给出一个数字,只能进行两种操作:
- 删除任意一个数字
- 从右边添加一个数字
最少进行多少种操作,让给出的数字变成2的N
次方?
思路:
-
如果数字本来就是2的
N
次方,操作0次(二进制法判断是否是2的n次方)
-
核心思路:打表存储 2 n 2^n 2n,对于每一个数,for循环枚举数组中的 2 n 2^n 2n,计算变成这些数字中最小需要几步
-
如何计算至少进行几步操作?删除的操作数+添加的操作数
-
寻找最大相同连续子串数目(双指针)
-
操作数=
a.length()-cnt+b.length()-cnt
-
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
string str[70];
bool check(int x)//判断是否是2的n次方
{
return x&(x-1)==0;
}
void init() //打表2^i
{
for(int i=0;i<=64;i++)
{
ll x =1ll*1<<i;//“*1ll”是因为1为int类型,防止等号后面爆int
str[i]=to_string(x);
}
}
int count(string a,string b)//双指针寻找最大相同连续子串数目,计算操作数目
{
int l1=a.length();
int l2=b.length();
int cnt=0;
for(int i=0,j=0;i<a.length();i++)
{
if(a[i]==b[j])
{
cnt++;
j++;
}
}
return l1-cnt+l2-cnt;
}
int main()
{
init();
int t;
cin>>t;
while (t--)
{
int n;
cin>>n;
if(check(n))
{
puts("0");
continue;
}else{
int ans=INF;
for(int i=0;i<=64;i++)//找出把给定数字变成2的i次方中最小操作数目
{
ans=min(ans,count(to_string(n),str[i]));
}
cout<<ans<<endl;
}
}
return 0;
}
E Polycarp and String Transformation(字符串拼接)
题意:
一个字符串t
,只能进行两种操作:
- 删除一种这个字符串中出现的字母,删除后的字符串为
s
- 拼接
t=t+s
直到字符串s
被删空,所有的字符都被删除。
题目会给定一个已经操作完毕后的字符串t
,要求输出它的原字符串,并且输出一个按照删除顺序形成的字符串
思路:
(1).我们会发现先删除的字符串在结果串t
中,最终出现的位置会更靠前,越往后删除的元素在结果串中的最终出现的位置越靠后。
所以我们从后往前遍历所有元素,从来没有出现过的元素一定是最后删除的元素, 放入数组中(所以数组是按删除顺序的倒序排列的)
(2).不同元素的个数就是删除的次数
(3).如果一个字符串一共进行了3次删减,串s
就空了
(4)删除的越早的元素出现的次数越少,原始字符的个数=结果串中出现的次数/(元素个数-删除数组下标)
。
(5).所有元素原始个数相加就是初始字符串的长度len
,截取给定字符串的前len
长度即为初始字符串string ss=s.sub_str(0,len)
(6).eg:结果串abacabaaacaac
删除顺序为cab
,在数组中顺序为bac
,下标为0,1,2
。
a
在结果串中出现的次数为8,删除次序为2,下标为1,所以会出现两次,原本a
的字符个数为8/(3-1)=4
。 其他类推
(7).如何判断答案不存在?
将计算出的原来的字符串ss
,按照给定的方式再拼接一次,自己拼结构的串为sss
,如果sss=s
,自己拼接后的串等于题目给定的拼接后的串,说明答案有效,否则输出-1
代码:
#include<iostream>
#include<map>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=5*1e5+10;
int main()
{
int t;
cin>>t;
while (t--)
{
string s;
cin>>s;
vector<char>p;
map<char,int>mp;
for(int i=s.size()-1;~i;i--)
{
if(!mp[s[i]]) p.push_back(s[i]);
mp[s[i]]++;
}
int len=0;
for(int i=0;i<p.size();i++)
{
len+=mp[p[i]]/(p.size()-i);
}
string ss=s.substr(0,len);//复原而成的原字符串
// cout<<"原来的"<<ss<<endl;
string sss=ss;//sss是拼结后的字符串
string t=ss;
for(int i=p.size()-1;~i;i--)
{
string add;
for(int j=0;j<t.size();j++)
{
if(p[i]!=t[j]) add+=t[j];
}
sss+=add;
t=add;
}
//cout<<"拼接后的"<<sss<<endl;
if(sss!=s) puts("-1");
else {
cout<<ss<<" ";
for(int i=p.size()-1;~i;i--)
cout<<p[i];
puts("");
}
}
return 0;
}