Codeforces Round #555 (Div. 3)[1157]题解

Posted --bluesky007

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #555 (Div. 3)[1157]题解相关的知识,希望对你有一定的参考价值。

不得不说这场div3是真的出的好,算得上是从我开始打开始最有趣的一场div3。因为自己的号全都蓝了,然后就把不经常打比赛的dreagonm的号借来打这场,然后...比赛结束rank11(帮dreagonm上蓝果然没有食言qwq)。

技术图片

(震惊...HA省A队CF青名...)

CF1157A Reachable Numbers

水题,分析一下不难发现不超过\\(10\\)次就会少一位,然后\\(10^9\\)范围内的数可以到达的数个数显然不多,不妨大力模拟,然后把出现的数扔进set,最后输出set.size()即可。(开到\\(10^6\\)不会T也不会有被卡次数的危险)

#include<bits/stdc++.h>

using namespace std;

int n;

set<int>s;

int main(){
    scanf("%d",&n);
    for(int i=1;i<=1e6;i++){
        s.insert(n);
        n+=1;
        while(n%10==0){
            n/=10;
        }
    }
    printf("%d\\n",(int)s.size());
    return 0;
}

CF1157B Long Number

注意是选择一段连续的区间,把区间内的数全部替换为映射值。不难发现可能会增大也有可能减小,为了让数尽可能变大,考虑采取贪心的策略,找到第一个能使得原数变大的位置\\(l\\),然后从这个位置向后找,找到第一个会变小的位置\\(r\\),则变化区间为\\([l,r)\\)

#include<bits/stdc++.h>

using namespace std;

const int N=2e5+5;

int n;

char s[N];

int a[15];

char ans[N],t[N];

int fir,lst;

int main(){
    scanf("%d",&n);
    scanf("%s",s+1);
    for(int i=1;i<=9;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=n;i++){
        if(s[i]-'0'<a[s[i]-'0']){
            fir=i;
            break;
        }
    }
    if(!fir){
        printf("%s\\n",s+1);
        return 0;
    }
    for(lst=fir;lst<=n;lst++){
        if(s[lst]-'0'>a[s[lst]-'0']){
            lst--;
            break;
        }
    }
    if(lst>n){
        lst=n;
    }
    for(int i=fir;i<=lst;i++){
        s[i]='0'+a[s[i]-'0'];
    }
    printf("%s\\n",s+1);
    return 0;
}

CF1157C Increasing Subsequence

C1

显然,贪心就行了,因为选完小的还能选大的,答案不会变劣。

#include<bits/stdc++.h>

using namespace std;

const int N=2e5+5;

int n;

int a[N],now,l,r;

char op[N];

int cntop;

int main(){
    scanf("%d",&n);
    l=1;r=n;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    while(l<=r){
        if(a[l]>now&&a[r]>now){
            if(a[l]<a[r]){
                op[++cntop]='L';
                now=a[l];
                l++;
            }
            else{
                op[++cntop]='R';
                now=a[r];
                r--;
            }
        }
        else if(a[l]>now){
            op[++cntop]='L';
            now=a[l];
            l++;
        }
        else if(a[r]>now){
            op[++cntop]='R';
            now=a[r];
            r--;
        }
        else{
            break;
        }
    }
    printf("%d\\n%s\\n",cntop,op+1);
    return 0;
}

C2

C1不一样的是,不保证任意两两不同。那么考虑和C1不同的点,不难发现在于两端的数的大小关系多了相等的情况,其他的也没什么不同。显然,如果两端相同,之后就只能从一边选,那么对于这种情况直接贪心选择最多的一边选即可。

#include<bits/stdc++.h>

using namespace std;

const int N=2e5+5;

int n;

int a[N],now,l,r;

char op[N];

int cntop;

int main(){
    scanf("%d",&n);
    l=1;r=n;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    while(l<=r){
        if(a[l]>now&&a[r]>now){
            if(a[l]==a[r]){
                for(int i=l+1,j=r-1;;i++,j--){
                    if(a[i]<=a[i-1]){
                        while(a[r]>now){
                            op[++cntop]='R';
                            now=a[r];
                            r--;
                        }
                        break;
                    }
                    else if(a[j]<=a[j+1]){
                        while(a[l]>now){
                            op[++cntop]='L';
                            now=a[l];
                            l++;
                        }
                        break;
                    }
                }
            }
            else if(a[l]<a[r]){
                op[++cntop]='L';
                now=a[l];
                l++;
            }
            else{
                op[++cntop]='R';
                now=a[r];
                r--;
            }
        }
        else if(a[l]>now){
            op[++cntop]='L';
            now=a[l];
            l++;
        }
        else if(a[r]>now){
            op[++cntop]='R';
            now=a[r];
            r--;
        }
        else{
            break;
        }
    }
    printf("%d\\n%s\\n",cntop,op+1);
    return 0;
}

CF1157D N Problems During K Days

这题挺有趣的,要求构造\\(k\\)个正整数和为\\(n\\),且满足对于\\(1\\leq i<k\\)\\(a_i<a_{i+1}\\leq2a_i\\)。从约束条件入手,分析下界可知,如果还有\\(k\\)个数需要确定,而第一个数为\\(i\\),那么这\\(k\\)个数在和最小的情况下分别是\\(i,i+1,\\cdots,i+k-1\\),由此可得下界。分析上界可知,和最大的情况下分别是\\(i,2i,2^2i,\\cdots,2^{k-1}i\\),和为\\((2^k-1)i\\),由此可得上界。因为第一个数可以无穷大但不能小于\\(1\\),所以约束主要是在下界,那么尽可能避免下界不合法即可,所以对于每个位置\\(i\\),使\\(a_i\\)尽可能小即可。以样例为例,n=8 k=3时,如果\\(a_1=1\\),那么和最大为\\(1+2+4=7\\),显然过小。如果\\(a_1=2\\),和最小为\\(2+3+4=9\\),又过大了,那么就不合法。n=9 k=4时,和最小为\\(1+2+3+4=10\\),亦过大。所以总的思路就是在和的上界不小于剩余的值的情况下选择最小的\\(a_i=\\max(\\lceil\\Large\\frac{n}{2^{k-i}-1}\\normalsize\\rceil,a_{i-1}+1)\\),然后判断在取得最小可能值的情况下会不会有下界不合法即可(因为取值已经保证上界合法)。

Trick:double在存储\\(2\\)的次方是可以得到精确值,用pow(2.0,x)就能得到精确的\\(2^x\\),就不需要再担心\\(\\large2^{10^5}\\)过大的问题。

#include<bits/stdc++.h>

using namespace std;

const int N=1e5+5;

int n,k;

long long sum(int u){
    return 1LL*u*(u+1)/2;
}

double sumb(int u){
    return pow(2.0,u)-1;
}

int a[N];

int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=k;i++){
        int rep=max(a[i-1]+1,(int)ceil((double)n/sumb(k+1-i)));
        if(sum(rep+k-i)-sum(rep-1)>n){
            printf("NO\\n");
            return 0;
        }
        a[i]=rep;
        n-=rep;
    }
    printf("YES\\n");
    for(int i=1;i<=k;i++){
        printf("%d%c",a[i]," \\n"[i==k]);
    }
    return 0;
}

CF1157E Minimum Array

题目意思是给出\\(a,b\\)两个数组,要求给\\(b\\)重新排序,使得\\(c_i=(a_i+b_i)\\bmod{n}\\)的字典序最小。显然字典序是由高位开始比较的,所以显然可以贪心解决,只需要让前面的\\(c_i\\)尽可能小即可。由模运算的性质可知,每次找到不小于\\(a_i\\bmod{n}\\)的相反数的最小数即可,如果不存在就选所有数的最小值。然后整个过程用multiset维护\\(b\\)数组即可。

#include<bits/stdc++.h>

using namespace std;

const int N=2e5+5;

int n;

int a[N],b[N];

multiset<int>s;

int c[N];

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=n;i++){
        scanf("%d",&b[i]);
        s.insert(b[i]);
    }
    for(int i=1;i<=n;i++){
        int rep=(n-a[i])%n;
        auto ite=s.lower_bound(rep);
        if(ite==s.end()){
            ite=s.begin();
        }
        c[i]=*ite;
        s.erase(ite);
    }
    for(int i=1;i<=n;i++){
        printf("%d%c",(a[i]+c[i])%n," \\n"[i==n]);
    }
    return 0;
}

CF1157F Maximum Balanced Circle

题目意思是从给定的\\(n\\)个数中选尽可能多的数,使得这些数按照某种顺序放到环上后,相邻两个数的差不超过\\(1\\)。显然对于这道题可以先桶排,然后考虑选出的数中最小的数,如果选出的数最小值和最大值分别为\\(l,r\\),那么一定需要按照\\(l,l+1,\\cdots,r,r-1,\\cdots,l\\)排序,否则相邻相差就会大于\\(1\\)。那么不难发现,对于\\(\\forall i\\in(l,r),cnt_i>1\\)。由此,可以考虑枚举\\(l\\),求出最大的\\(r\\),显然,对于\\(l'\\in(l,r]\\),如果将选出的数范围改为\\([l',r]\\),那么答案一定会比\\([l,r]\\)劣,所以枚举的时间复杂度为\\(O(\\max a_i)\\)。然后考虑一下细节,处理\\(l=r,l+1=r,l+1<r\\)三种情况即可。

#include<bits/stdc++.h>

using namespace std;

const int N=2e5+5;

int n,a[N],c[N],sum[N];

int ans;

int mx,l,r;

int query(int u,int v){
    return sum[v]-sum[u-1];
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        ++c[a[i]];
    }
    for(int i=1;i<2e5;i++){
        if(c[i]&&c[i+1]){
            ans=i;
            break;
        }
    }
    if(!ans){
        for(int i=1;i<=2e5;i++){
            if(c[i]>mx){
                ans=i;
                mx=c[i];
            }
        }
        printf("%d\\n",mx);
        for(int i=1;i<=mx;i++){
            printf("%d%c",ans," \\n"[i==mx]);
        }
        return 0;
    }
    for(int i=1;i<=2e5;i++){
        sum[i]=sum[i-1]+c[i];
    }
    for(int i=1,j;i<=2e5;){
        if(!c[i]||!c[i+1]){
            i++;
            continue;
        }
        else if(c[i+1]==1){
            if(mx<query(i,i+1)){
                mx=query(i,i+1);
                l=i;r=i+1;
            }
            i++;
        }
        else{
            for(j=i+1;j<=n;j++){
                if(c[j]>1){
                    continue;
                }
                else{
                    break;
                }
            }
            if(!c[j]){
                --j;
            }
            if(mx<query(i,j)){
                mx=query(i,j);
                l=i;r=j;
            }
            i=j;
        }
    }
    printf("%d\\n",mx);
    for(int i=l;i<=r;i++){
        printf("%d ",i);
    }
    for(int i=r;i>=l;i--){
        for(int j=1;j<c[i];j++){
            printf("%d ",i);
        }
    }
    return 0;
}

CF1157G Inverse of Rows and Columns

写前面的时候蠢了,耽误了一些时间,没来得及写,等早上再补。

以上是关于Codeforces Round #555 (Div. 3)[1157]题解的主要内容,如果未能解决你的问题,请参考以下文章

E Minimum Array ( Codeforces Round #555 (Div. 3) )

Codeforces Round #555 div3 C2

Codeforces Round #555 (Div. 3)[1157]题解

codeforces 555b//Case of Fugitive// Codeforces Round #310(Div. 1)

Codeforces Round # 555 (Div. 3) C2. Increasing subsequence (complicated version) (贪心)

Codeforces Round #555 (Div. 3) A B C1(很水的题目)