Educational Codeforces Round 66 Rated for Div. 2
A. From Hero to Zero
题意:
给定一个n和一个k,通过以下操作将n转换为0
- n=n-1
- 若n可以整除k,n = n/2
输出从n转换到0所需要的 最小次数
思路:
除k的情况时贡献度最高的,要优先考虑除k。
通过直接减去当前n对k的余数,节省减一操作的时间。
代码简化:
利用整形变量抹去小数点的性质,每次操作增加当前n对k的余数加1后,n更新为n除k。最后对于答案减去多余的一次除操作。
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
cin>>t;
while(t--){
long long n,k;
cin>>n>>k;
long long ans=0;
while(n){
long long a=n%k;
n/=k;
ans+=a+1;
}
cout<<ans-1<<endl;
}
return 0;
}
B. Catch Overflow!
题意:
给出以下模式的伪代码,模拟循环累加操作。
- for n(1≤n≤100)——表示进行n次循环
- end——表示循环体结束
- add——表示进行一次累加
给出的伪代码中,add能出现在循环体内外,for之后可以马上接end(无循环体),保证for 和end 都能对应上。
若最后答案没有超过 2 32 − 1 2 ^ {32} -1 232−1,输出准确的累加答案;否则,输出“OVERFLOW!!!”
思路:
模拟,利用栈对于循环次数的累乘进行统计,利用两个标记来对整个模拟进行控制。
一旦累计循环次数比
2
32
−
1
2 ^ {32} -1
232−1大则做出标记Ⅰ。
当标记Ⅰ存在时,循环累乘结果不再需要记录实际值,若出现add,则做出标记Ⅱ。
出现end时,弹出栈顶并统计累加结果;若存在标记Ⅱ,则不需要进一步计算。
最后根据标记Ⅱ的有无,决定输出的种类
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAx = 4294967296;
stack<ll>s;
int main()
{
int n;
s.push(1);
while(cin>>n)
{
ll f=1;
ll ans=0;
ll flag1=0;
bool flag=0;
while(n--)
{
char c[4];
cin>>c;
if(c[0]=='a')
{
if(ans+s.top()>=MAx||flag1>0)
{flag=1;continue;}
ans+=s.top();
}
if(c[0]=='f')
{
ll a;
cin>>a;
if(s.top()>=MAx||a*s.top()>=MAx){flag1++;s.push(MAx);}//使用标记Ⅰ记录超额累乘for的个数
else s.push(a*s.top());
}
if(c[0]=='e')
{
if(flag1!=0)flag1--;
s.pop();
}
}
if(flag)
cout<<"OVERFLOW!!!"<<endl;
else
cout<<ans<<endl;
}
return 0;
}
C. Electrification
题意:
给出数轴上的n个点a1~an,存在一个点x到ai的距离为di。
将得到的n个di进行升序排序,得到一个第k大的距离dk+1
输出在dk+1尽量小的情况下,x的坐标
思路:
初始的dk+1表示的是x到ak+1的距离,但在排序后dk+1则是第k大的距离,二者是不一样的。所以我们需要构造出第k大的距离,换而言之,存在k-1个距离比它小或等于,存在n-k个距离比它大或等于。
我们假设点x为点ai到ai+k这段线段的中点,则在点ai到ai+k存在的k-1个点到x的距离,都会比ai到x小;反之,在两点之外的n-k-1的距离就会比它大,加上ai+k到x的距离就存在n-k个距离的条件。
所以ai到x的距离为所求的dk+1。此时,我们只限于找到ai到ai+k最短的一根线段,并求出中点(中位数)即可。
#include<bits/stdc++.h>
using namespace std;
int main(){
int T;
cin>>T;
while(T--){
int n,k;
cin>>n>>k;
long long a[200005];
for(int i=0;i<n;i++){
cin>>a[i];
}
long long MIN=a[k]-a[0];
long long l=0,r=k;
for(int i=1;(i+k)<n;i++){
if(a[i+k]-a[i]<MIN){
MIN=a[i+k]-a[i];
l=i;r=i+k;
}
}
cout<<(a[r]+a[l]+1)/2<<endl;
}
return 0;
}
D. Array Splitting
题意:
给出一个数组,要求将这个数组分成k组,每组产生一个cost。
假设第i组的求和是Si ,它的cost就是
S
<
s
u
b
>
i
<
/
s
u
b
>
∗
i
S<sub>i</sub>*i
S<sub>i</sub>∗i
输出k组cost求和最大的情况。
思路:
题目要求构造出
∑
S
<
s
u
b
>
i
<
/
s
u
b
>
∗
i
∑S<sub>i</sub>*i
∑S<sub>i</sub>∗i最大值。与其从头开始判断每一组怎么去才能最大,不如直接求出
∑
S
<
s
u
b
>
i
<
/
s
u
b
>
∗
k
∑S<sub>i</sub>*k
∑S<sub>i</sub>∗k再减去k-1个前缀和。
只要每次都贪心减去当前最小的前缀和,就能的到所求
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[1000005];
ll s[1000005]={0};
int main()
{
int n,k;
while(cin>>n>>k){
for(int i=0;i<n;i++){
cin>>a[i];
}
s[0]=a[0];
for(int i=1;i<n;i++){
s[i]=a[i]+s[i-1];
}
s[n-1]*=k;
sort(s,s+n-1);
for(int i=0;i<k-1;i++){
s[n-1]-=s[i];
}
cout<<s[n-1]<<endl;
}
}