Educational Codeforces Round 125 (Rated for Div. 2) A B C D
Time:2022/3/23 3:35
A - Integer Moves
思路: 无非就是想什么情况$(0,0)$到$(x,y)$的距离为整数,显然用勾股定理算一下即可,小学数学。特判一下$(0,0)$,唯一的坑点样例给出了。其余情况如果距离是整数那么输出$1$否则先横移再竖移即输出$2$即可。
- 参考代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
void solve(){
int a,b;
cin>>a>>b;
int c=a*a+b*b;
if(a==0&&b==0){
cout<<0<<endl;
return ;
}
c=sqrt(c);
if(c*c==a*a+b*b){
cout<<1<<endl;
}
else cout<<2<<endl;
}
signed main(){
int t=1;
cin>>t;
for(int i=1;i<=t;i++){
solve();
}
return 0;
}
B - XY Sequence
思路: 一眼贪心加减模拟即可,数据很大记得开$long$ $long$,题目怎么说怎么做即可。
- 参考代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5+10;
int a[N];
void solve(){
int n,b,x,y;
cin>>n>>b>>x>>y;
a[0]=0;//全局即为0可以省略
int sum=0;
for(int i=1;i<=n;i++){
if(a[i-1]+x>b){
a[i]=a[i-1]-y;
}
else{
a[i]=a[i-1]+x;
}
sum+=a[i];
}
cout<<sum<<endl;
}
signed main(){
int t=1;
cin>>t;
for(int i=1;i<=t;i++){
solve();
}
return 0;
}
C - Bracket Sequence Deletion
思路: 括号配对问题,先是好好理解题目什么情况下能消掉,即最小的$good$ $prefix$,也就是只要出现了$good$ $prefix$一定要删掉。好好读懂题目也就不难理解为什么第三个样例是$2$ $0$。那么怎么做呢? 分类讨论,什么情况下可以两个两个消掉?$1:(($ $2:()$ $3:))$。那么有一种情况是比较特殊的,即$)($不能直接消掉,什么时候能消掉呢?直到碰到下一个$)$,例如:$)(((((()$构成回文,此时才能消掉,那么剩下的模拟即可。
- 参考代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
void solve(){
int n;
cin>>n;
string s;
cin>>s;
int sum1,sum2;
sum1=sum2=0;//记录(和)数量
int all=0;//操作次数
int ed=0;//删除之后下一次坐标
for(int i=0;i<s.size();i++){
if(sum1==1){//((
if(s[i]=='('){
all++;
ed=i+1;
sum1=0;
sum2=0;
}
else if(s[i]==')'){//()
all++;
ed=i+1;
sum1=0;
sum2=0;
}
}
else if(sum2==1){//))
if(s[i]==')'){
sum1=0;
sum2=0;
ed=i+1;
all++;
}
else if(s[i]=='('){//)(
bool ok = false;
int j;
for(j=i+1;j<s.size();j++){
if(s[j]==')'){
ok=true;
ed=j+1;
break;
}
}
sum1=sum2=0;
if(!ok){
goto GG;
}
else {i=j;all++;}
}
}
else if(s[i]=='(')sum1++;
else if(s[i]==')')sum2++;
}
GG:;
cout<<all<<' '<<s.size()-ed<<endl;
}
signed main(){
int t=1;
cin>>t;
for(int i=1;i<=t;i++){
solve();
}
return 0;
}
D - For Gamers. By Gamers.
思路: 赛时假了,赛后刚补,首先看到$5e5$会往$O(nlogn)$级别的做法处想,赛时我的想法是二分,事实证明确实用得到二分,只不过在此之前需要通过调和级数将所有佣兵的花费倍率一直枚举到最大不超过$C$为止。为啥要用到调和级数呢? 首先将本题解法思路进行整理:我们要求每场战斗雇佣的佣兵(数量不限只要钱管够),以比怪兽战胜佣兵更快的速度战胜怪兽(严格$>$)的情况下,求花费金币数量最小是多少,如果无解输出$-1$。那么假设佣兵的攻击是a,血条是b,怪兽的攻击是c,血条是$d$。那么我们要求有$\left[\dfrac{d}{a}\right]$$<$$\left[\dfrac{b}{c}\right]$ $\Rightarrow$ $a$ * $b>c$ * $d$。那么我们首先记入初始给出的$n$个$coin$($coin$可能相等)能获得的最大战斗力的佣兵,我们记 $a$ * $b$ 的值为战斗力。然后用到调和级数的性质,将已知的$coin$所对应的不同战斗力的佣兵进行增加至不大于$m$,例如:$2coin$士兵对应的战斗力为$5$,$4coin$士兵对应的战斗力为$6$,显然$4coin$进行更新可以更优$\Rightarrow$$4coin$士兵对应的战斗力变为$10$。调和级数时间复杂度$(O(nlogn))$。然后将$1$~$C$所有$coin$所能所得战斗力取一个最大值。进而进行二分查找,时间复杂度$(O(nlogn))$,总时间复杂度$(O(nlogn))$。参考代码加注释,易于理解。
- 参考代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,s;
const int N = 1e6+10;
int a[N];
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);//数据比较大,如果用cin,cout建议开加速器
cin>>n>>s;
for(int i=1;i<=n;i++){
int c,d,h;
cin>>c>>d>>h;
a[c]=max(a[c],d*h);//初始每c个金币所能所得最大战斗力
}
for(int i=1;i<=s;i++){
if(!a[i])continue;//如果i个金币战斗力未知,则跳过。
for(int j=2;i*j<=s;j++){//否则进行调和级数的更新
a[i*j]=max(a[i*j],a[i]*j);
}
}
for(int i=1;i<=s;i++)a[i]=max(a[i-1],a[i]);//将1~C所有coin所能所得战斗力取一个最大值
int t;
cin>>t;
while(t--){//开始二分
int x,y;
cin>>x>>y;
x=x*y;
if(x>=a[s])cout<<"-1"<<endl;//如果最多金币的战斗还是小于等于怪兽即输出-1
else{//否则二分查找答案
int l=1,r=s;
while(l<r){
int mid=l+r>>1;
if(a[mid]>x)r=mid;
else l=mid+1;
}
cout<<l<<' ';
}
}
return 0;
}
巨巨tql