2024.4.5 训练1(VP) Codeforces Round 905 (Div. 3)
Posted on 2024-04-06 00:27 qiujian222 阅读(17) 评论(0) 编辑 收藏 举报题目链接: Codeforces Round 905 (Div. 3)
参考博客:https://zhuanlan.zhihu.com/p/663407494
B题
没有分析清楚问题,匆匆忙忙去分类讨论,一段巨复杂的分类讨论代码调了半小时硬卡过去了。
赛时AC代码:https://codeforces.com/contest/1883/submission/255043239
分析:首先来分析一下哪些字符必须删除,哪些可以保留。显然,偶数个数的字符都不必删除,奇数个数的字符要删到最多一个或者全删。而k次删除必须全部用完,分三种情况:
- k不足以将奇数个字符删到只剩一个,该情况为NO
- k刚好删到只剩一个奇数个字符,YES
- k刚好删除所有奇数个字符,YES
- k删掉所有奇数个字符后还有多,这个时候发现,对于全是偶数的字符串,删除多少个都是回文的,所以YES
合并上面的条件,如果有sum个奇数字符,k>=sum-1
就行
AC代码:
#include<bits/stdc++.h>
#define ll long long
#define db double
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
ll t; cin>>t;
while(t--){
map<ll,ll> hash;
ll n,k; string s; ll sum=0;
cin>>n>>k>>s;
for(auto x:s) hash[x]++;
for(auto [x,y]:hash) if(y&1) sum++;
if(k>=sum-1) cout<<"YES"<<"\n";
else cout<<"NO"<<"\n";
}
return 0;
}
C题
太急了,题目没看完全,k的范围极小。比赛的时候写出了正确的代码但被k==4卡了,想到原因是两个2可以凑一个4,然后想到其他的非质数都可以凑然后复杂度爆炸大脑雪崩。其实k特判一下4就行。赛后写代码还是不能丝滑写出特判4的情况,可以说思路整个就很难在正轨上
分析:整个数列中有k就能被整除,所以思路是通过+1的操作产生k。k的范围中除了4都是质数,也就是说k!=4的时候在数列中找到与k绝对值的差最小的数即可,k不会由其他数组成,只需考虑本身。但4例外,4可以由两个2组成或者一个2两个1等等情况组成,会有许多情况,这时要避免分类讨论。当我们考虑数列中的奇数和偶数个数时,显而易见的,两个偶数的积一定能被4整除,一个奇数加上1就是偶数,这样有下面的组合:
数列中有两个以上偶数 ans=0
数列中只有1个偶数和1个及以上的奇数 ans=1
数列中没有偶数有2个及以上的奇数 ans=2
数列中没有偶数只有1个奇数 ans和k!=4的答案一致
AC代码:
#include<bits/stdc++.h>
#define ll long long
#define db double
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
ll t; cin>>t;
while(t--){
ll n,k ;
cin>>n>>k;
vector<ll> a(n);
ll sum1=0,sum2=0,ans=0;
for(int i=0;i<n;i++){
cin>>a[i];
if(a[i]&1) sum1++;
else sum2++;
if(a[i]<=k) a[i]=abs(a[i]-k);
else if(a[i]%k==0) a[i]=0;
else a[i]=abs(k-a[i]%k);
}
sort(a.begin(), a.end());
ans=a[0];
if(k==4){
if(sum2>=2) ans=0;
else if(sum2==1&&sum1>=1) ans=min(ans,1ll);
else if(sum2==0&&sum1>=2) ans=min(ans,2ll);
}
cout<<ans<<"\n";
}
return 0;
}
D题
赛后补题,考察裸的multiset运用和区间判定的思想。将每一个操作的区间l、r分别存入两个multiset L、R,删除直接查找它们。每次判断L的最右边元素是否大于
R的最左边元素即可。
multiset补充:https://www.cnblogs.com/ChinaHook/p/6985444.html
#include<bits/stdc++.h>
#define ll long long
#define db double
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
ll t; cin>>t;
multiset<ll> L,R;
while(t--){
char op; ll l,r;
cin>>op>>l>>r;
if(op=='+'){
L.insert(l);
R.insert(r);
}
else{
L.erase(L.find(l));
R.erase(R.find(r));
}
if(!L.empty()&&*L.rbegin()>*R.begin()) cout<<"YES"<<"\n";
else cout<<"NO"<<"\n";
}
return 0;
}
E题
题目很像之前牛客小白月赛的一道题,除了那题是a=a+1之外一模一样。一开始按照之前的思路写贪心,觉得比A题还简单,但是模拟每个数乘之后状态爆long long了过不去。网上都说模拟会爆TLE?看了很多博主的题解都很复杂,找到一个非常简洁但是有点难懂的解法(主要也是博主没怎么解释,硬看出来的,佬大概不屑于解释太多)
设一个pre代表累计乘2之后的积(实际上pre统计的是乘2的次数,约等于表示乘积)。在图中可以看出,\(l_1\)代表的是\(a_2\)为了达到大于等于\(a_1\)所乘了一些2后的积。因为\(a_3\)更小,所以\(a_3\)可以继承\(a_2\)之前乘的一些2,只需要乘更少的2,也就是\(l_2\),然后pre加上多乘2的次数,那么此时的pre代表的就是\(l_1+l_2\)。后面\(a_4\)更大,但是pre要往后继承,\(a_5\)要想达到\(a_4\)的高度并不需要乘那么多的2,因为之前已经乘过了,让\(l_1+l_2\)在\(a_4\)上面进行抵消,同时减小pre。如果抵消之后\(a_4\)与\(a_5\)持平,那么pre也减到了0,表示前面的累乘用完了,此时\(a_5\)不用乘任何的2,下一个重新开始累乘。
AC代码:
#include<bits/stdc++.h>
#define ll long long
#define db double
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
ll t; cin>>t;
while(t--){
ll n; cin>>n;
vector<ll> a(n+1);
ll ans=0,pre=0;
for(int i=1;i<=n;i++){
cin>>a[i]; ll x=a[i];
if(x<a[i-1]) while(x<a[i-1]) x*=2,pre++;
else while(x>=a[i-1]*2&&pre) x/=2,pre--;
ans+=pre;
}
cout<<ans<<"\n";
}
return 0;
}