导航

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次删除必须全部用完,分三种情况:

  1. k不足以将奇数个字符删到只剩一个,该情况为NO
  2. k刚好删到只剩一个奇数个字符,YES
  3. k刚好删除所有奇数个字符,YES
  4. 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;
}