Educational Codeforces Round 125 (Rated for Div. 2)
A. Integer Moves
Example
input
3
8 6
0 0
9 15
output
1
0
2
题目大意:
问在坐标中,从原点走到一个点最少需要几步,其中,一步的长度必须是整数。
思路:
数学。
该点是原点时,输出0,该点到原点的距离就是整数时,输出1.
当该点到原点的距离不是整数时,最少只需要两步就可以走到。以各自两个点为圆心画一个圆,两圆相交的时候,肯定存在这么一个交点,使得两个圆的半径都是整数。所以输出2即可。
代码:
#include<iostream>
#define endl '\n'
using namespace std;
const int N=1e5+7;
void solve()
{
int x,y;
cin>>x>>y;
int z=sqrt(x*x+y*y);
if(x==0&&y==0)cout<<0<<endl;
else if(z*z==x*x+y*y)cout<<1<<endl;
else cout<<2<<endl;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
cin>>t;
while(t--)
{
solve();
}
return 0;
}
B. XY Sequence
Example
input
3
5 100 1 30
7 1000000000 1000000000 1000000000
4 1 7 3
output
15
4000000000
-10
题目大意:
构造一个数列,每个元素数值不能大于B,第一个元素是0,每个元素的值是是前一个元素+x或者-y,求数列最大和。
思路:
贪心,模拟。
从左到右,只要不超过B,就+x,否则-y,求和即可。
代码:
#include<iostream>
#define endl '\n'
using namespace std;
const int N=1e5+7;
void solve()
{
int n,b,x,y;
cin>>n>>b>>x>>y;
long long ans=0;
long long sum=0;
for(int i=0;i<n;++i)
{
if(sum+x>b)
{
sum-=y;
}
else
{
sum+=x;
}
ans+=sum;
}
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
cin>>t;
while(t--)
{
solve();
}
return 0;
}
C. Bracket Sequence Deletion
Example
input
5
2
()
3
())
4
((((
5
)((()
6
)((()(
output
1 0
1 1
2 0
1 0
1 1
题目大意:
给一个由左右括号组成的序列,每次从左往右看,若前缀满足1.是一个合法括号序列2.是一个至少长为2的回文,就删去它,求最后删除的次数和留下的最短序列的长度。
思路:
贪心+模拟。
可以分为几种情况。
1.前两个字符是 “()”,它是一个合法括号序列,此时直接删去。
2.前两个字符是 “((”,它是一个符合要求的回文,直接删去。
3.前两个字符是 “))”,它是一个符合要求的回文,直接删去。
4.前两个字符是 “)(”,它都不符合,因为右括号已经出现在了前面,所以它已经不可能构成一个合法的括号序列了。所以它只能是一个回文,那么什么时候构成回文呢?当后面又出现一个右括号的时候就构成了一个回文(因为此时两个右括号之间夹着一堆左括号,肯定是回文)。
代码:
#include<iostream>
#define endl '\n'
using namespace std;
const int N=1e6+7;
void solve()
{
int n;
cin>>n;
string a;
cin>>a;
int ans=0;
int r=n;
for(int i=0;i<n;++i)
{
if(n-i>=2&&a[i]=='(')
{
++ans;
++i;
r-=2;
}
else
{
if(a[i]==')')
{
bool flag=1;
for(int j=i+1;j<n;++j)
{
if(a[j]==')')
{
flag=0;
++ans;
i=j;
r=n-j-1;
break;
}
}
if(flag)break;//后面没有右括号了
}
}
}
cout<<ans<<' '<<r<<endl;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
cin>>t;
while(t--)
{
solve();
}
return 0;
}
D. For Gamers. By Gamers.
Examples
input
3 10
3 4 6
5 5 5
10 3 4
3
8 3
5 4
10 15
output
5 3 -1
input
5 15
14 10 3
9 2 2
10 4 3
7 3 5
4 3 1
6
11 2
1 1
4 7
2 1
1 14
3 3
output
14 4 14 4 7 7
input
5 13
13 1 9
6 4 5
12 18 4
9 13 2
5 4 5
2
16 3
6 2
output
12 5
题目大意:
你有n种兵,每种兵都有自己的费用c,每秒攻击力d(攻击是持续的,不是回合制),血量h,你有C个金币。有m个怪物,每个怪物有自己的每秒攻击力D,血量H。对每一个怪物,你需要分别买兵杀死怪物,但只能买一种,不限量,并且你不能让自己有兵死掉,问最少花费。
思路:
数学,二分。
因为攻击是持续的,所以只要杀死怪物的时间比有兵被杀死的时间小就行。即h/D>H/(d*x),即 x * h * d > H * D
而花费为 c * x ,c,H和D是固定的,所以要让x尽量小,所以要让h*d尽量大,但并不是选h * d最大的那个就是最优的,因为攻击是持续的,不是离散的。
我们记录每种花费能买到的最大的h * d,再让h * d随花费递增,再二分查找,找到对应的花费。
代码:
#include<iostream>
#define endl '\n'
using namespace std;
const int N=1e6+7;
long long cost[N];//花费i买一个队的最大d*h
long long f[N];//花费i可以买到的最大d*h,一个队可能买了几次
void solve()
{
int n,C;
cin>>n>>C;
for(int i=0;i<n;++i)
{
long long c,d,h;
cin>>c>>d>>h;
cost[c]=max(cost[c],d*h);
}
for(int i=1;i<=C;++i)//花费i可以获得的最大d*h
{
if(cost[i])
{
for(int j=1;i*j<=C;++j)//这种买j个的最大,d*h*j
{
f[i*j]=max(f[i*j],cost[i]*j);
}
}
}
for(int i=1;i<=C;++i)
{
f[i]=max(f[i],f[i-1]);//递增,我用i-1的钱就可以买到这么多d*h,i肯定能买到
}
int m;
cin>>m;
while(m--)
{
long long H,D;
cin>>D>>H;
int p=upper_bound(f,f+C+1,D*H)-f;//二分查找第一个大于H*D的花费价格,+1方便处理不存在的情况
if(p<=C)cout<<p<<' ';
else cout<<-1<<' ';
}
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
while(t--)
{
solve();
}
return 0;
}