starrycoding周赛
title: starrycoding周赛
categories:
- ICPC
tags:
- null
abbrlink: 1828b237
date: 2023-04-01 00:00:00
E题:题意:给定一个数组,你有两个操作,第一个操作是对任意前缀减去一个数,第二个操作是对任意后缀减去一个数。现在希望你用这两个操作将这两个数都变成0,要求操作次数最少,无解情况需要判断。
Sol:考虑两种操作,一种前缀操作,一种后缀操作都是对一整个区间操作,我们应该想到区间加常用的差分!我们记d为差分数组
操作1等价于$d[1]-=k和d[i+1]+=k$
操作2等价于$d[n+1]+=k和d[n-i+1]-=k$
题目要求我们用最少操作次数完成原数组恰好全部置0,等价于差分数组变0.
问题转化到这逐渐清晰。
考虑d[n+1]是不影响答案的,也就是操作二使用没有代价,对于差分数组中的所有正元素全部可以免费变0
对于为0的不用管
对于负元素,我们需要用操作1将其变0,考虑代价是次数加1以及d[1]减小,如果d[1]不够减,则原问题无解。
void solve(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=n;i>=1;i--)a[i]-=a[i-1];
int sum=0;int cnt=0;
for(int i=2;i<=n;i++){
//cerr<<a[i]<<" ";
if(a[i]<0){sum+=abs(a[i]);}
if(a[i]!=0)cnt++;
}
// cerr<<endl;
if(sum>a[1])cout<<-1<<endl;
else if(sum==a[1])cout<<cnt<<endl;
else cout<<cnt+1<<endl;
}
C题:有n个数每次取出最大的数x,算它的bitsum(x),然后x-=bitsum(x),这样反复k次,求第k次取出的数的bitsum.
Sol:由于k的范围最大到1e9,所以我们很难用优先队列去模拟这个过程,注意到初始的数值域只有1e6,我们考虑维护一个桶统计每个值出现的次数,从大到小遍历,次数累加到转移的数上面,直到k不够减。注意特判k过大的情况,直接输出0.
int cal(int x){
int res=0;
string s=to_string(x);
for(int i=0;i<s.size();i++)res+=s[i]-'0';
return res;
}
//很好的维护手段
void solve(){
//批量维护值域
int k;cin>>n>>k;
for(int i=1;i<=n;i++){int x;cin>>x;a[x]++;}
for(int i=1e6;i>=0;i--){
if(a[i]<k){
k-=a[i];
a[i-cal(i)]+=a[i];
}
else {
cout<<cal(i)<<endl;
return ;
}
}
cout<<0<<endl;
}
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 爱飞鱼的blog!