CF Round 1023 (Div. 2)补题
Codeforces Round 1023 (Div. 2) 补题
此次排名情况:
- 共8k+,排名1651,做出ABC三题。
- Rating+81,当前为
1442。 - 比赛链接
A. LRC and VIP
题意:有一个长度为 n 的数组 a ,你需要将数组分成 2 个序列,满足一下条件:
- 每个元素只能属于二者之一
- 每个序列至少包含一个元素
- 两个序列全部GCD不相等
题解:当数组 a 中的元素全部相等时,不论序列如何分,两序列gcd相等,不可能划分。
比较数组 a 中的最大值和最小值,如果最大值和最小值相等,输出 no。
否则将最大值一个元素作为序列,其余的作为另一个序列即可。
因为 \(gcd(a,b)≤min(a,b)\) ,其余的元素的 gcd 肯定比最大值小,且不相等。
//获得最大值
m=*max_element(a.begin(),a.end());
//获得最小值
n=*min_element(a.begin(),a.end());
void solve(){
int n;cin>>n;
vector<int> a(n);
for(int i=0;i<n;i++) cin>>a[i];
//获得最大值最小值
int mn=*min_element(a.begin(),a.end());
int mx=*max_element(a.begin(),a.end());
if(mn==mx){cout<<"No\n";return;}
cout<<"Yes\n";
for(int i=0;i<n;i++)
cout<<(1+(a[i]==mx))<<"\n";
}
B. Apples in Boxes
题意:有一个长度为 n 的数组 a ,两个人轮流操作。
- 选择一个 a 中的大于 0 的元素,使这个 \(a_i\) 减 1。
- 如果没有可以减的元素,当前人输。
- 如果操作后,数组中最大值 - 最小值大于 k ,那么当前人输。
题解:
假设 \(max(a)-min(a)≤k\) 成立,并且至少有一个 \(a_i≥1\)。那么可以在保持这个条件的情况下拿走一个苹果。
我们从最大元素中减去,最小元素不会发生变化。即 \(max(a)\) 减少 0 或者减少 1,\(max(a)-min(a)\) 只会减少,与 k 进行比较显然成立。有一个特例,即所有元素都相同的情况,操作后极值的差由 0 变成 1。
因此,对于一个有 \(max(a)-min(a)≤k\) 的数组来说,唯一输的方式,就是将所有的 \(a_i\) 全部输掉,全部等于 0。执行 \(\sum_{i=1}^{n} a_i\) 次操作后全部输掉,因为每个回合都只会减少 1 。
如果一开始数组就不成立,直接输出第一个人。
void solve(){
int n,k;cin>>n>>k;
vector<int> a(n);
ll sum=0;
for(int i=0;i<n;i++) cin>>a[i],sum+=a[i];
sort(a.begin(),a.end());
a[n-1]--;//最大值-1
sort(a.begin(),a.end());
if(a[n-1]-a[0]>k||sum%2==0) cout<<"Jerry\n";
else cout<<"Tom\n";
}
C. Maximum Subarray Sum
题意:给你一个长度为 n 的数组 a 和一个正整数 k,数组中有些元素可以任意更改,使最大子数组和正好是 k 。
题解:
不可能的情况:将可替换元素全部改为 -INF,如果最大子数组和仍然大于 k ,答案不可能。还有,如果没有可替换元素,且最大子数组和不为 k ,答案不可能。
其余皆有可能。
将可替换元素全部改为 -INF,然后选某个可替换元素,计算此元素前面的最大子数组和,计算后面的最大子数组和,然后将这个元素更改为某个值,使最大子数组和为 k 即可。
void solve(){
ll n,k;cin>>n>>k;
string s;cin>>s;
vector<ll> a(n);
for(int i=0;i<n;i++) cin>>a[i];
int pos=-1;
for(int i=0;i<n;i++)
if(s[i]=='0')
pos=i,a[i]=-1e13;
ll mx=0,cur=0;
for(int i=0;i<n;i++){
cur=max(cur+a[i],a[i]);
mx=max(mx,cur);
}
if(mx>k||(mx!=k&&pos==-1)){
cout<<"No\n";
return;
}
if(pos!=-1){
mx=0,cur=0;
ll l,r;
for(int i=pos+1;i<n;i++){
cur+=a[i];
mx=max(mx,cur);
}
l=mx;
mx=0,cur=0;
for(int i=pos-1;i>=0;i--){
cur+=a[i];
mx=max(mx,cur);
}
r=mx;
a[pos]=k-l-r;
}
cout<<"Yes\n";
for(int i=0;i<n;i++){
cout<<a[i]<<" ";
}
cout<<"\n";
}
D. Apple Tree Traversing
题意:有一棵 n 个节点的树,每个节点上有一个苹果。你可以进行如下操作:
- 选择一条路径 (u,v),这条路径每个节点上都有一个苹果。
- 假设 d 是路径上苹果的个数,依次写下三个数字 (d,u,v)。
- 然后去掉这条路径上所有的苹果
使写下的序列最大,输出序列。
题解:题解复杂度为 \(O(nlogn)\)。
一棵树中的最长路径为树的直径,可以使用树形DP或者搜索求解。
假设 \(f_i,g_i\) 是 \(i\) 的子树中从 \(i\) 出发的最长和第二长路径,
则树的直径为 \(max_{i=1}^n f_i+g_i\)。
当我们找到直径 \((u,v)\) 时,让我们更新路径 \((lca(u,v),root)\) 上所有 \(u\) 的直径 \(f_u,g_u\)。
使用 set 为每个节点 \(u\) 查找 \(f_u,g_u\) ,并使用 priority_queue 保持最大 \(f_i+g_i\)。总复杂度为 \(O(nlogn)\)。

