//https://img2018.cnblogs.com/blog/1646268/201908/1646268-20190806114008215-138720377.jpg

2023.8.31 CF 解题报告

2023.8.30 CF1863 解题报告

只有前四道的解题报告。

E 太难了,半夜做题脑子容易抽,不会。

A

题意

多组数据。

每组数据给你三个数 \(n, a, q\)\(n\) 表示当前人数,\(a\) 是初始在线人数,\(q\) 是在线人数发生的变化。

下面一行只有 \(-+\) 号,\(+\) 表示有一个人上线,\(-\) 表示有一个人下线,但不知道具体是谁。

求是一定 YES \(n\) 个人都上过线,还是可能 MAYBE \(n\) 个人都上过线,还是不可能 NO \(n\) 个人都上过线。

Solution

我们考虑什么时候才能确定这三种情况。

我们顺序读入字符,开两个变量 \(cnt1, cnt2\) 记录上下线人数。

  • 如果要是 \(a+cnt1-cnt2\ge n\),那么一定是所有人都上过线了,因为有一个时刻所有人都在线。
  • 如果要是 \(a+cnt1\ge n\),那么可能所有人都上过线了,可能每一次上线的都是没上过线的。
  • 不属于上面两种的就是不可能。

具体细节看代码。

/*
 * @Author: Aisaka_Taiga
 * @Date: 2023-08-30 22:44:27
 * @LastEditTime: 2023-08-30 22:46:07
 * @LastEditors: Aisaka_Taiga
 * @FilePath: \Desktop\CFA.cpp
 * 心比天高,命比纸薄。
 */
#include <bits/stdc++.h>

#define int long long
#define N 1000100

using namespace std;

int t, n, cnt1, cnt2, a, q;
char s;

signed main()
{
    cin >> t;
    while(t --)
    {
        cin >> n >> a >> q;
        int ff = 0;
        if(a >= n) ff = 1;
        cnt1 = cnt2 = 0;
        for(int i = 1; i <= q; i ++)
        {
            cin >> s;
            if(s == '+') cnt1 ++;
            if(s == '-') cnt2 ++;
            if(a + cnt1 - cnt2 >= n) ff = 1;
            if(a + cnt1 >= n && !ff) ff = 2;
        }
        if(ff == 0) cout << "NO" << endl;
        if(ff == 1) cout << "YES" << endl;
        if(ff == 2) cout << "MAYBE" << endl;
    }
    return 0;
}

T2

题意

多组数据。

给你一个长度为 \(n\) 的序列,每一次可以选一个数 \(x\),进行以下操作:

  • 把小于 \(x\) 的给按顺序写下。
  • 把大于等于 \(x\) 再按顺序写到上面操作的序列后。

求最少的次数让序列变成从小到大有序的。

保证给的序列是 \(1\sim n\) 的一个排列。

Solution

我们发现如果一个数,可以被比他小的数变成有序的的话,就没有必要选这个数,手模一下样例也可以看出选数的先后顺序对答案没有影响。

我们考虑如何最优化。

记录一个 \(cnt\),初始值为 \(n\)

我们从序列最右段开始扫,如果遇到了一个 \(a_{i} = cnt\),那么我们把 \(cnt\) 减一,如果能还找到 \(cnt\) 的话,说明上一个 \(cnt\) 值,如果选了当前的 \(cnt\) 可以被排成有序的。

然后扫到最左端,当前的 \(cnt\) 就是我们第一个操作的数。

直到 \(cnt = 0\),我们就可以排好序了,此时最少的操作次数就是我们扫了几遍这个序列减一,因为最后一次扫的部分实际上已经有序了。

我们开一个 \(pos\) 记录每一个数的出现位置,然后给他判断比他小一的是不是在他左边,然后就代替了我们扫一遍这 \(n\) 个数的过程,复杂度也从 \(O(n^2)\) 降到了 \(O(n)\)

具体细节看代码。

/*
 * @Author: Aisaka_Taiga
 * @Date: 2023-08-30 23:17:01
 * @LastEditTime: 2023-08-30 23:29:10
 * @LastEditors: Aisaka_Taiga
 * @FilePath: \Desktop\CFB.cpp
 * 心比天高,命比纸薄。
 */
#include <bits/stdc++.h>

#define int long long
#define N 1000100

using namespace std;

int t, n, a[N], vis[N];

signed main()
{
    cin >> t;
    while(t --)
    {
        cin >> n;
        for(int i = 1; i <= n; i ++) cin >> a[i], vis[a[i]] = i;
        int cc = n, cnt = 0;
        while(1)
        {
            while(vis[cc - 1] < vis[cc]) cc --;
            cc --;
            cnt ++;
            if(cc == -1) break;
        }
        cout << cnt - 1 << endl;
    }
    return 0;
}
// 10 19 7 1 17 11 8 5 12 9 4 18 14 2 6 15 3 16 13
// 19,17,14,10,7,5,4,1

T3

题意

多组数据。

给你一个长度为 \(n\) 的序列,里面是互不相同的 \(0\sim n\) 的整数,定义一次操作为:

对于序列里的每一个数 \(a_{i}\),将他替换成序列中最小的未出现的自然数。

然后求进行 \(k\) 次操作后的序列。

Solution

我们手模一下样例里的 0 1 3

发现进行一次操作后是 2 0 1

进行第二次操作后是 3 2 0

进行第三次操作后是 1 3 2

进行第四次后就是 0 1 3

我们发现是循环的,我们把 \(2\) 加到一开始的里面,得到:

0 1 3 2
2 0 1 3
3 2 0 1
1 3 2 0
0 1 3 2

循环起来了,而且都往右平移了一位。

具体细节看代码:

/*
 * @Author: Aisaka_Taiga
 * @Date: 2023-08-30 23:47:51
 * @LastEditTime: 2023-08-30 23:48:03
 * @LastEditors: Aisaka_Taiga
 * @FilePath: \Desktop\CFC.cpp
 * 心比天高,命比纸薄。
 */
#include <bits/stdc++.h>

#define int long long
#define N 1000100

using namespace std;

int t, n, m, a[N], ans[N], vis[N];

signed main()
{
    cin >> t;
    while(t --)
    {
        cin >> n >> m;
        m %= n + 1;
        int cao = -1;
        for(int i = 0; i <= n; i ++) vis[i] = 0;
        for(int i = 1; i <= n; i ++) cin >> a[i], vis[a[i]] ++;
        for(int i = 0; i <= n; i ++) if(!vis[i]) cao = i;
        a[n + 1] = cao;
        for(int i = 1; i <= n + 1; i ++) ans[(m + i) % (n + 1)] = a[i];
        for(int i = 1; i <= n ; i ++) cout << ans[i] << " ";
        cout << endl;
    }
    return 0;
}

T4

题意

给你一个 \(n\times m\) 的棋盘,上面有一些 \(1\times 2\) 的多米诺骨牌,整个棋盘用三种字符表示:

  • . 表示没有东西。
  • U 表示一块竖着放置的多米诺骨牌上半部分。
  • D 表示一块竖着放置的多米诺骨牌下半部分。
  • L 表示一块横着放置的多米诺骨牌左半部分。
  • R 表示一块横着放置的多米诺骨牌右半部分。

你需要把里面的棋盘有骨牌的格子染成黑白两色,要求一个骨牌里两块格子的颜色不能相同,棋盘每行每列的黑白格子数相等。

保证输入合法,误解输出 -1

Solution

因为多米诺骨牌怎么染都是一黑一白,所以我们这样考虑。

对于一行,我们可以发现横着的对他没有影响。反而是竖着的需要考虑。

对于一列,我们发现竖着的对他没有影响,我们只需要考虑横着的。

然后我们来考虑一个构造,我们直接一个双重循环,对于行,我们只要遇到 U 或者 D,我们就染色,因为一张骨牌要一黑一白,我们顺便把与他相连的染色,然后打上标记,我们染色是按照“黑白黑白黑白”这样交替染色,如果染的首尾格子颜色一样就说明无解。

对于列也同理,不再多做赘述。

这样做为什么是对的?

先来说行。

考虑我们在染色的过程中如果不是上面的无解情况的话,染完的骨牌一定合法,延伸到下一行也是一定合法,因为打了标记,所以下一行遍历不会考虑到,然后就相当于没有那些骨牌,所以后面只要不是上面无解的情况就一定合法。

对于列也是同理。

code:

/*
 * @Author: Aisaka_Taiga
 * @Date: 2023-08-31 00:44:12
 * @LastEditTime: 2023-08-31 07:38:42
 * @LastEditors: Aisaka_Taiga
 * @FilePath: \Desktop\CFD.cpp
 * 心比天高,命比纸薄。
 */
#include <bits/stdc++.h>

#define int long long
#define N 510

using namespace std;

int t, n, m, vis[N][N];
char a[N][N], b[N][N];

signed main()
{
    cin >> t;
    while(t --)
    {
        int ff = 0;
        // memset(vis, 0, sizeof vis);
        cin >> n >> m;
        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= m; j ++)
                cin >> a[i][j], vis[i][j] = 0;
        for(int i = 1; i <= n; i ++)
        {
            int t = 1;
            for(int j = 1; j <= m; j ++)
            {
                if(vis[i][j]) continue;
                if(a[i][j] == '.') b[i][j] = '.', vis[i][j] = 1;
                if(a[i][j] == 'U')
                {
                    b[i][j] = (t ^ 1 ? 'W' : 'B');
                    b[i + 1][j] = (t ? 'W' : 'B');
                    vis[i][j] = vis[i + 1][j] = 1;
                    t ^= 1;
                }
            }
            if(t == 0) ff = 1;
        }
        for(int j = 1; j <= m; j ++)
        {
            int t = 1;
            for(int i = 1; i <= n; i ++)
            {
                if(vis[i][j]) continue;
                if(a[i][j] == '.') b[i][j] = '.', vis[i][j] = 1;
                if(a[i][j] == 'L')
                {
                    b[i][j] = (t ^ 1 ? 'W' : 'B');
                    b[i][j + 1] = (t ? 'W' : 'B');
                    vis[i][j] = vis[i][j + 1] = 1;
                    t ^= 1;
                }
            }
            if(t == 0) ff = 1;
        }
        if(ff == 1) {cout << "-1" << endl; continue;}
        for(int i = 1; i <= n; i ++)
        {
            for(int j = 1; j <= m; j ++)
                cout << b[i][j];
            cout << endl;
        }
    }
    return 0;
}
/*
..BW..
BBWW..
WWBBBW
..WBWB
-1
..
..
*/
posted @ 2023-08-31 17:31  北烛青澜  阅读(14)  评论(0编辑  收藏  举报