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
..
..
*/
本文来自博客园,作者:北烛青澜,转载请注明原文链接:https://www.cnblogs.com/Multitree/articles/17670095.html
The heart is higher than the sky, and life is thinner than paper.