Codeforces Round #541 (Div. 2) A~F题解

A. Sea Battle

  • 题意
    给你两个矩形的大小,求出包围两个矩形的小方块的个数,即绿色的部分。
    在这里插入图片描述

  • 解题思路
    很容易得知,这个大小为 2 ∗ w 1 + 2 ∗ h 1 + 4 + 2 ∗ h 2 2 * w1 + 2 * h1 + 4 + 2 * h2 2w1+2h1+4+2h2

  • AC代码

/**
  *@filename:A
  *@author: pursuit
  *@created: 2021-09-09 16:24
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl

using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;

int w1, h1, w2, h2;
void solve(){
    int ans = 2 * w1 + 2 * h1 + 4 + 2 * h2;
    cout << ans << endl;
}
int main(){	
    cin >> w1 >> h1 >> w2 >> h2;
    solve();
    return 0;
}

B. Draw!

  • 题意
    两个球队进行比赛,初始得分比为: 0 : 0 0:0 0:0,按照时间顺序给出部分比分结果,最后一个作为结束使得结果。问比赛过程中最多有多少次比分相同。

  • 解题思路
    直接遍历得分情况,即如果当前的得分比为 a [ i ] : b [ i ] a[i]:b[i] a[i]:b[i],之前的得分比为 a [ i − 1 ] : b [ i − 1 ] a[i -1]:b[i-1] a[i1]:b[i1]。那么我们能上涨的变化为 m i n ( a [ i ] , b [ i ] ) − m a x ( a [ i − 1 ] , b [ i − 1 ] ) min(a[i],b[i])-max(a[i-1],b[i-1]) min(a[i],b[i])max(a[i1],b[i1]),那么方案数自然为 m i n ( a [ i ] , b [ i ] ) − m a x ( a [ i − 1 ] , b [ i − 1 ] ) + 1 min(a[i],b[i])-max(a[i-1],b[i-1])+1 min(a[i],b[i])max(a[i1],b[i1])+1,当然,我们要考虑为负数的情况,所以需要与 0 0 0 max ⁡ \max max
    这里需要注意的一点就是为了避免重复计算 a [ i ] = b [ i ] a[i]=b[i] a[i]=b[i]的情况,所以碰到这种我们需要将 a [ i ] + + a[i]++ a[i]++

  • AC代码

/**
  *@filename:B
  *@author: pursuit
  *@created: 2021-09-09 16:28
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl

using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e4 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;

int n, a[N], b[N];
void solve(){
    ll ans = 0;
    for(int i = 1; i <= n; ++ i){
        ans += max(0, min(a[i], b[i]) - max(a[i - 1], b[i - 1]) + 1);
        if(a[i] == b[i]){
            //该点已经被计算过。
            ++ a[i];
        }
    }
    printf("%lld\n", ans);
}
int main(){	
    scanf("%d", &n);
    for(int i = 1; i <= n; ++ i){
        scanf("%d%d", &a[i], &b[i]);
    }
    solve();
    return 0;
}

C. Birthday

  • 题意
    给定 n n n个人的身高,使其围成一个环,要求相邻两个人的所有身高差最大值最小。

  • AC代码
    贪心的去思考,由于成环,所以每个人都会与其他两个人相邻,而如果我们将这些人的身高排序,那么如果直接成环,那么最大差值自然是 a [ n ] − a [ 1 ] a[n]-a[1] a[n]a[1],这肯定是最坏的结果。所以我们需要减小这个最大值,那么自然是安排最大值与最大值相邻,通过双端队列依次分配到左右两边,这样能确保每个人的相邻位置差在排序数组中不会超过 2 2 2

  • AC代码

/**
  *@filename:C
  *@author: pursuit
  *@created: 2021-09-09 16:44
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl

using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;

int n, a[N];
void solve(){
    sort(a + 1, a + n + 1);
    int l = n - 1, r = n;
    deque<int> q;
    if(n & 1){
        q.push_back(a[n]);
        -- l, -- r;
    }
    while(l >= 1){
        q.push_back(a[r]);
        q.push_front(a[l]);
        l -= 2;
        r -= 2;
    }
    while(!q.empty()){
        printf("%d ", q.front());
        q.pop_front();
    }
    puts("");
}
int main(){	
    scanf("%d", &n);
    for(int i = 1; i <= n; ++ i){
        scanf("%d", &a[i]);
    }
    solve();
    return 0;
}

D. Gourmet choice

  • 题意
    给定 a a a序列和 b b b序列的对应元素大小关系矩阵 s s s,其中 s i j s_{ij} sij表示 a a a中的第 i i i个元素与 b b b中的第 j j j个元素的大小关系。请你判断是否能根据此矩阵构造出 a a a b b b,使得两者序列元素中的最大值最小。

  • AC代码
    我们可以将这种大小关系看成图,即如果相等,那么则可以缩成一个点,点中的每个元素值都是相同的,这个可以通过并查集实现;如果 a [ i ] > b [ j ] a[i]>b[j] a[i]>b[j],那么可以看成有一条从 j j j i i i的有向边;如果 a [ i ] < b [ j ] a[i]<b[j] a[i]<b[j],那么可以看成有一条从 i i i j j j的有向边,然后利用拓扑排序解决此题即可。
    注意 b b b序列中的元素编号需要做出 n n n的偏移量。

  • AC代码

/**
  *@filename:D
  *@author: pursuit
  *@created: 2021-09-09 17:50
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl

using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e3 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;

int n, m;
char s[N][N];
int father[N << 1];
int in[N << 1], res[N << 1];
vector<int> g[N << 1];
int q[N << 1], st, ed;
int find(int x){
    int r = x;
    while(father[r] != r)r = father[r];
    int i = x, j;
    while(father[i] != r){
        j = father[i], father[i] = r, i = j;
    }
    return r;
}
void solve(){
    //考虑如果a > b则a -> b否则a <- b;
    for(int i = 1; i <= n; ++ i){
        for(int j = 1; j <= m; ++ j){
            if(s[i][j] == '>'){
                //a > b;
                g[find(j + n)].push_back(find(i));
                ++ in[find(i)];
            }
            else if(s[i][j] == '<'){
                //a < b;
                g[find(i)].push_back(find(j + n));
                ++ in[find(j + n)];
            }
        }
    }
    //考虑利用topoSort。
    st = 1, ed = 0;
    for(int i = 1; i <= n + m; ++ i){
        if(!in[i]){
            q[++ ed] = i;
            res[i] = 1;
        }
    }
    while(st <= ed){
        int u = q[st ++];
        for(auto v : g[u]){
            res[v] = max(res[v], res[u] + 1);
            -- in[v];
            if(!in[v]){
                q[++ ed] = v;
            }
        }
    }
    for(int i = 1; i <= n + m; ++ i){
        if(in[i]){
            puts("No");
            return;
        }
    }
    puts("Yes");
    for(int i = 1; i <= n; ++ i){
        printf("%d ", res[find(i)]);
    }
    puts("");
    for(int i = n + 1; i <= n + m; ++ i){
        printf("%d ", res[find(i)]);
    }
    puts("");
}
int main(){	
    scanf("%d%d", &n ,&m);
    for(int i = 1; i <= n + m; ++ i){
        father[i] = i;
    }
    for(int i = 1; i <= n; ++ i){
        scanf("%s", s[i] + 1);
        for(int j = 1; j <= m; ++ j){
            //为等号的即是相同的,我们将其合并在一起。
            if(s[i][j] == '='){
                father[find(i)] = father[find(j + n)];
            }
        }
    }
    solve();
    return 0;
}

E. String Multiplication

  • 题意
    定义一种字符串乘法: s ∗ t = t + s 1 + t + s 2 + … + t + s m + t s*t=t + s_1 + t + s_2 + \ldots + t + s_m + t st=t+s1+t+s2++t+sm+t。给出 n n n个字符串 p 1 , p 2 , p 3 , … , p n p_1, p_2, p_3, \ldots, p_n p1,p2,p3,,pn,现在需要你求出通过 ( … ( ( ( p 1 ⋅ p 2 ) ⋅ p 3 ) ⋅ … ) ⋅ p n ( \ldots (((p_1 \cdot p_2) \cdot p_3) \cdot \ldots ) \cdot p_n ((((p1p2)p3))pn形成的字符串中连续的相同字母的最大长度。

  • 解题思路
    我们单独看字符串乘法,实际上就是第二个字符串插在第一个字符串的字符间隔处。所以实际上我们并不在乎第一个字符串的内容,仅仅在乎于它能贡献什么,由于不同字母的最大长度不一样,所以我们要考虑 26 26 26个字母;这里以考虑字符a为例,如果第一个字符串中不存在a,那么当前最大长度实际上就是第二个字符串贡献的;而如果第一个字符串中存在a,实际上就是我们求的前 i − 1 i-1 i1个字符串相乘得到的最大长度 c n t > 0 cnt>0 cnt>0,那么它能与第二个字符串中组合贡献,即如果第二个字符串全为a,那么我们可以得到 c n t + l e n ∗ ( c n t + 1 ) cnt+len*(cnt+1) cnt+len(cnt+1);如果第二个字符串不全是a,那么当前形成的最大值可来源于第二个字符串的最大长度或者第二个字符串的前缀aa与后缀a拼接形成,取 max ⁡ \max max即可。

  • AC代码

/**
  *@filename:E
  *@author: pursuit
  *@created: 2021-09-09 21:10
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl

using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;

int n;
string s[N];
//abc * de = deadebdecde
//计算s1 * s2 * s3 * ... * sn;
int cal(char c, int idx){
    int res = 0, cnt = 0;
    for(int i = 0; i < s[idx].size(); ++ i){
        if(s[idx][i] == c){
            ++ cnt;
        }
        else{
            cnt = 0;
        }
        res = max(res, cnt);
    }
    return res;
}
bool check(char c, int idx){
    for(auto x : s[idx]){
        if(c != x)return false;
    }
    return true;
}
int back(char c, int idx){
    int cnt = 0;
    for(int i = s[idx].size() - 1; i >= 0; -- i){
        if(s[idx][i] == c)++ cnt;
        else break;
    }
    return cnt;
}
int front(char c, int idx){
    int cnt = 0;
    for(int i = 0; i < s[idx].size(); ++ i){
        if(s[idx][i] == c)++ cnt;
        else break;
    }
    return cnt;
}
void solve(){
    ll res = 0;
    for(char c = 'a'; c <= 'z'; ++ c){
        ll cnt = 0;
        for(int i = 1; i <= n; ++ i){
            if(cnt == 0){
                cnt = cal(c, i);
            }
            else{
                //判断是否全是a。
                if(check(c, i)){
                    cnt = cnt + (cnt + 1) * s[i].size();
                }
                else{
                    //否则取最大值。
                    cnt = max(cal(c, i), 1 + back(c, i) + front(c, i));
                }
            }
        }
        res = max(res, cnt);
    }
    cout << res << endl;
}
int main(){	
    cin >> n;
    for(int i = 1; i <= n; ++ i){
        cin >> s[i];
    }
    solve();
    return 0;
}

F. Asya And Kittens

  • 题意
    有一个由 1 1 1~ n n n构成的长度为n的序列,相邻两数不连通,经过 n − 1 n-1 n1次操作,整个序列是连通的,每次操作只能将相邻两块进行连通,给出每次操作时相邻两块中的数字,输出原序列。

  • 解题思路
    这个题实际上就是连通块合并,只不过这个连通块有一个特性就是连通块存在左端点和右端点。初始的时候每个点的连通块左右端点都是其本身。
    关键在于合并过程,两个连通块合并,那么第一个连通块的右端点必然为第二个连通块的右端点,当然这里我们还需要保存的就是元素的串接关系,即内部元素的指向关系,这个我们需要引入一个 n e x t next next值处理即可。
    其余的按正常并查集处理即可。

  • AC代码

/**
  *@filename:F
  *@author: pursuit
  *@created: 2021-09-09 16:58
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl

using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N = 2e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;

int n, father[N];
struct node{
    int l, r, next;
}nodes[N];
int find(int x){
    int r = x;
    while(father[r] != r){
        r = father[r];
    }
    int i = x, j;
    while(father[i] != r){
        j = father[i];
        father[i] = r;
        i = j;
    }
    return r;
}
void solve(){
    int u, v, fu, fv;
    for(int i = 1; i < n; ++ i){
        scanf("%d%d", &u, &v);
        fu = find(u), fv = find(v);
        father[fu] = fv;
        nodes[nodes[fv].r].next = nodes[fu].l;
        nodes[fv].r = nodes[fu].r;
    }
    int root = nodes[find(1)].l;
    while(root){
        printf("%d ", root);
        root = nodes[root].next;
    }
    puts("");
}
int main(){	
    scanf("%d", &n);
    for(int i = 1; i <= n; ++ i){
        father[i] = nodes[i].l = nodes[i].r = i;
    }
    solve();
    return 0;
}
posted @   unique_pursuit  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
点击右上角即可分享
微信分享提示