Codeforces Educational 127 A-E vp补题

CF Edu 127 A-D vp补题

继续每日一vp,今天晚上有课,时间不太多,回去就直接vp。前三题比较简单,过了之后排名rk2000+,然后就去洗澡了。d题没怎么认真思考,其实也可做。最后rk4000+。发挥还行,b题罚时4次确实不应该。继续加油!
题目链接

A. String Building 签到
题意:给你一个只含‘a’和‘b’的字符串,问你能否通过‘aa’,‘aaa’,‘bb’,‘bbb’来组成。
思路:很明显直接判断字符串中是否存在单独的‘a’或者‘b’即可。
经典的双指针写法

void Showball(){
   string s;
   cin>>s;
   for(int i=0;i<s.size();i++){
     int j=i;
     while(j<s.size()-1&&s[j]==s[j+1]) j++;
     if(i==j) {cout<<"NO"<<endl;return;}
     i=j;
   }
   cout<<"YES"<<endl;
}

B. Consecutive Points Segment 贪心
题意:给你一个严格递增的序列,你可以对每一个数x,进行x+1,x-1或者不变的操作。问你能否把序列构造成形如 l , l + 1 , l + 2 , . . . , l + n − 1 l,l+1,l+2,...,l+n-1 l,l+1,l+2,...,l+n1的连续的序列。
思路:直接贪心分析,第一个数的情况比较特殊,单独分析,一旦前面的数确定就不能改变了,我们每次尽可能让数变大,如果相邻差值超过3,那么无法进行构造。具体看代码。当时写的时候细节没有处理好,wa了4发,(气急败坏!)

void Showball(){
  int n;
  cin>>n;
  vector<int> a(n);
  for(int i=0;i<n;i++) cin>>a[i];
  for(int i=0;i<n-1;i++){
        int t=a[i+1]-a[i];
        if(!i){
            if(t==0) a[i+1]++;
            else if(t==1) a[i]++,a[i+1]++;
            else if(t==2) a[i]++;
            else if(t==3) a[i]++,a[i+1]--;
            else {cout<<"NO"<<endl;return;}
        }else{
           if(t==0) a[i+1]++;
           else if(t==1) continue;
           else if(t==2) a[i+1]--;
           else {cout<<"NO"<<endl;return;}
        }
  }
  cout<<"YES"<<endl;
}

C. Dolce Vita 前缀和+贪心
题意:你每天都有x元,一开始给你n个物品的价格,每天所有的物品的价格都会+1。问你一共最多可以买几件物品。
思路:这个题算法本场高光了,看完题立马有了思路,简单理了一下逻辑直接就开码了。(码之前还提醒自己这题要开long long!)
然后很快,样例过了。交上去…看到一直在过数据,直呼稳了。然后wa 5。以为贪心错了,结果看了一下,vector没有开long long。(下次注意!
回归正题:这个题,贪心,我们就先从小到大给商品排个序。然后求一下前缀和。由于我们要算一共能买几件商品,我们直接统计每件商品最多能买几次,再求和就可以。对于第 i i i个物品,如果x t / i + 1 t/i+1 t/i+1次。因为我们排了序,所以如果t>0,说明第一次我们可以直接买到1-i这些商品,t就是剩下的钱。因为每天每件商品会涨价1元。所以 t / i t/i t/i就是剩下的钱能够在涨价后还能买几天。因为第一天没有涨价,所以还要加上第一天的一次。最后统计就ok。代码十分简洁!

void Showball(){
   LL n,x;
   cin>>n>>x;
   vector<LL> a(n+1);
   LL sum=0;//写题解时,发现前缀和边算边用
   for(int i=1;i<=n;i++) cin>>a[i];
   sort(a.begin()+1,a.end());
   LL ans=0;
   for(int i=1;i<=n;i++){
    sum+=a[i];
    LL t=x-sum;
    if(t>=0) ans+=(t/i+1);
   }
   cout<<ans<<endl;
}

D. Insert a Progression 贪心+思维
题意:给你一个序列和一个正整数x。你需要将1-x之间的数插入到这个序列中去(可以插入到开头和结尾),然后求出最小的相邻两项绝对值之差的总和。
思路:经过分析,我们很快可以发现一些性质,对于单调的区间,插入后维持单调性,是不会影响结果的。比如在2和7之间插入3 4 5 6。结果还是5。反过来也如此,进行拓展我们就可以发现在序列最大值和最小值之间的数,可以直接插入并且对结果没有影响。接下来我们考虑这之外的数如何处理。我们设序列最大值为 m a x n maxn maxn,最小值为 m i n n minn minn。通过刚才的分析,我们发现,对于 1 1 1 m i n n − 1 minn-1 minn1这些数,如果我们按照连续的顺序插入,其实和直接插入1的结果是一样的,例如我们将1,2,3插入到4 6 7这个序列中。1,2,3,4,6,7。可以发现符合前面讲的单调的性质,所以2,3这些元素对结果不影响。所以我们只需要考虑1的插入。同理,我们也只需要考虑x的插入。
对1插入的位置进行分析。无非就是开头,结尾和 m i n n minn minn的旁边。
对于开头的情况,我们发现对答案的贡献是 m a x ( a 0 − 1 , 0 ) max(a_0-1,0) max(a01,0)。对于结尾的情况。贡献则是 m a x ( a n − 1 − 1 , 0 ) max(a_{n-1}-1,0) max(an11,0)。对于最小值旁边的情况,贡献则是 2 ∗ m a x ( m i n n − 1 , 0 ) 2*max(minn-1,0) 2max(minn1,0)。因为左边贡献一次,右边贡献一次。不理解的话,选三个数手算一下,就明白了。对于x的插入分析同理。

LL a[N];
void Showball(){
  LL n,x;
  cin>>n>>x;
  for(int i=0;i<n;i++) cin>>a[i];
  if(n==1){
    cout<<max(a[0],x)-1<<endl;
    return;
  }
  LL ans=0;
  LL minn=*min_element(a,a+n);
  LL maxn=*max_element(a,a+n);
  for(int i=0;i<n-1;i++){
    ans+=abs(a[i]-a[i+1]);
  }
  LL t1=min({2*max(0ll,x-maxn),max(0ll,x-a[0]),max(0ll,x-a[n-1])});
  LL t2=min({2*max(0ll,minn-1),max(0ll,a[0]-1),max(0ll,a[n-1]-1)});
  cout<<ans+t1+t2<<endl;
}

E. Preorder思维+计数
题意:给你一个满二叉树,每个节点都有一个字符‘A’或者‘B’。然后总的字符串就是从根节点按照序号连起来的字符。你可以将一个非叶子节点的左二子和右儿子交换。问你一共能够构成多种字符串。
思路:题目也很清晰。我们知道当两个儿子不同的时候,那么就可以有两种情况。所以我们就可以递归处理,如果左边和右边的字符串不相同答案就会乘2。因此我们需要较快的去比较左右字符串的大小。因此我们就可以进行规定,每次组合都按照字典序进行组合,那么对于两个一样情况的字符串,我们就可以直接进行比较了。
ps:对于char数组 使用

char s[N];
cin>>s+1;

这样读入在c++20里是不被允许的,会报错。一定要使用的话,可以循环读入字符,或者使用std::string

char s[N];
int n;
int f[N];
//快速幂 a^b mod p
LL qmi(int a, int b, int p)
{
    LL res = 1 % p;
    while (b)
    {
        if (b & 1) res = res * a % p;
        a = a * (LL)a % p;
        b >>= 1;
    }
    return res;
}
string dfs(int u){
    if(!s[u<<1]) return s[u]=='A'?"A":"B";
    string s1=dfs(u<<1),s2=dfs(u<<1|1);
    f[u]=f[u<<1]+f[u<<1|1];
    if(s1!=s2) f[u]++;
    if(s1<s2) return s1+s[u]+s2;
    else return s2+s[u]+s1;
}
void Showball(){ 
  s[0]='?';
  cin>>n;
  for(int i=1;i<1<<n;i++) cin>>s[i];
  dfs(1);
  cout<<qmi(2,f[1],mod)<<endl;
}

完结撒花!!!

你可能感兴趣的:(算法题解,算法)