Codeforces Round #738 (Div. 2) 题解(A-D1)

本文详细解析了Codeforces Round #738 (Div.2)的四道题目,包括数组操作的最大值优化、字符串相邻字符匹配、图的路径规划以及有向图的增边问题。通过深入理解题意,运用贪心策略和并查集等算法,给出了高效的解题思路和代码实现,帮助读者提升编程竞赛解题能力。

Codeforces Round #738 (Div. 2) 题解(A-D1)

A. Mocha and Math

题目大意:

存在一个长度为 n n n的整数数组,可以选择任意一个区间 [ l , r ] [l,r] [l,r]对于所有的 i ( 0 ≤ i ≤ r − l ) i(0\le i\le r-l) i(0irl),将 a i a_i ai变成 a l + i & a r − i a_{l+i}\And a_{r-i} al+i&ari,这种操作可以执行无数次。

问整个数组中最大值能取到的最小值是多少。

解题思路:

其实这个操作看似很复杂,但我们都知道任何两个数 A & B ≤ min ⁡ ( A , B ) A\And B\le \min(A,B) A&Bmin(A,B),所以无论选取那个区间执行操作都不会使得区间的最大值变大,那么对于每个数而言都可以跟其他的数 & \And &一次,所以每个数能取到的最小值就是所有数相与的结果。

所以答案就是所有整数与起来的结果。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=110;
int a[N];
int n;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        int res=a[1];
        for(int i=2;i<=n;i++) res&=a[i];
        printf("%d\n",res);
    }
    return 0;
}

B. Mocha and Red and Blue

题目大意:

给出一个长度为 n n n的字符串,要使得整个字符串的相邻字符相同的次数尽可能少的出现,字符串只由 B , R , ? B,R,? B,R,?三种构成,其中 ? ? 表示这个字符可以由我们指定为 B B B R R R中的任意一种,输出最终的字符串。

解题思路:

首先我们能只能决定字符串中 ? ? ?的字符,如果对于一连串的 ? ? ?,我们肯定是选择采取BR两种字符间隔的构造字符串,那么这段字符串中间是不可能出现下相邻字符相同的,出现冲突的地方只有可能出现在两端。那我们只要根据某一端已经确定好的字符,选择与这个字符不同的作为起始字符构造就行了。

如果想要证明这种贪心做法是正确的,可以自己根据两头字符是否相同、连续 ? ? ?字符串的长度是奇是偶来分四种情况证明,这里就不再赘述了。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=110;
char s[N];
char a[2]={'B','R'};
int n;
void draw(int x,int c){
    for(int i=x;i>=1;i--){
        if(s[i]!='?') break;
        s[i]=a[c];
        c^=1;
    }
}
void draw2(int x,int c){
    for(int i=x,cnt=0;i<=n;i++){
        if(s[i]!='?') break;
        s[i]=a[c];
        c^=1
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        scanf("%s",s+1);
        char last='R';
        int p=0;
        for(int i=1;i<=n;i++){
            if(s[i]!='?'){
                draw(i-1,s[i]=='B');
                last=s[i];
                p=i;
            }
        }
        if(s[n]=='?') draw2(p+1,last=='B');
        printf("%s\n",s+1);
    }
    return 0;
}

C. Mocha and Hiking

题目大意:

图中存在 n + 1 n+1 n+1个点和 2 n + 1 2n +1 2n+1条有向边边。

n − 1 n-1 n1条边是由 i i i指向 i + 1 i+1 i+1,其中 1 ≤ i ≤ n − 1 1\le i\le n-1 1in1

其余的 n n n条边由一个长度为 n n n序列 a a a给出,如果 a i = 0 a_i=0 ai=0,那么就是由 i i i指向 n + 1 n+1 n+1,否则就是由 n + 1 n+1 n+1指向 i i i

问能否从某个点出发不重不漏地经过所有点,输出具体的方案。

解题思路:

看到题的第一反应以为是欧拉路径,但其实这题跟欧拉路径没什么关系。

因为前n-1条边的原因,我们可以不重不漏的从 1 1 1走到 n n n

那么我们只需要考虑将 n + 1 n+1 n+1这个点加入到 1 1 1 n n n的所有情况就行了。

  • n + 1 n+1 n+1作为起点,只需要有从 n + 1 n+1 n+1 1 1 1的边就行了,即 a 1 = 1 a_1=1 a1=1
  • n + 1 n+1 n+1作为中间点插入到 i i i i + 1 i+1 i+1之间,那么只需要有 i → n + 1 i\rightarrow n+1 in+1 n + 1 → i + 1 n+1\rightarrow i+1 n+1i+1两条边就行了,即 a i = 0 , a i + 1 = 1 a_i=0,a_{i+1}=1 ai=0,ai+1=1
  • n + 1 n+1 n+1作为终点,只需要有一条从 n n n n + 1 n+1 n+1的边就行了,即 a n = 0 a_n=0 an=0

如果这三种都行不通,就无解。

时间复杂度 O ( n ) O(n) O(n)

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int a[N];
int n;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        if(a[1]){
            printf("%d ",n+1);
            for(int i=1;i<=n;i++) printf("%d ",i);
            puts("");
        }
        else{
            int p=-1;
            a[n+1]=1;
            for(int i=1;i<=n;i++)
                if(!a[i]&&a[i+1]){
                    p=i;
                    break;
                }
            if(p==-1) puts("-1");
            else{
                for(int i=1;i<=n;i++){
                    printf("%d ",i);
                    if(i==p) printf("%d ",n+1);
                }
                puts("");
            }
        }

    }
    return 0;
}

D. Mocha and Diana (Easy Version)

题目大意:

有两个由 n n n个顶点组成的图,其中两个图分别有 m 1 m_1 m1 m 2 m_2 m2条初始边,对任意一个图进行加边的同时另一个图会加一条相同的边,问两个图中不出现的环前提下最多能加多少条边。

1 ≤ n ≤ 1000 1\le n \le 1000 1n1000

解题思路:

因为数据比较小,所以可以通过维护两个并查集,然后直接用 n 2 n^2 n2的暴力就行了。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int p1[N],p2[N];
int n,m1,m2;
int find(int x,int p[]){
    if(p[x]!=x) p[x]=find(p[x],p);
    return p[x];
}
int main()
{
    scanf("%d%d%d",&n,&m1,&m2);
    for(int i=1;i<=n;i++){
         p1[i]=p2[i]=i;
    }
    while(m1--){
        int a,b;
        scanf("%d%d",&a,&b);
        int fa=find(a,p1),fb=find(b,p1);
        p1[fa]=fb;
    }
    while(m2--){
        int a,b;
        scanf("%d%d",&a,&b);
        int fa=find(a,p2),fb=find(b,p2);
        p2[fa]=fb;
    }
    vector<pair<int,int> > v;
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            int pa1=find(i,p1),pb1=find(j,p1);
            int pa2=find(i,p2),pb2=find(j,p2);
            if(pa1!=pb1&&pa2!=pb2){
                v.push_back({i,j});
                p1[pa1]=pb1;
                p2[pa2]=pb2;
            }
        }
    }
    printf("%d\n",v.size());
    for(auto i:v) printf("%d %d\n",i.first,i.second);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值