Codeforces Round #744 (Div. 3) 题解
文章目录
A. Casimir’s String Solitaire
-
题意
给你一个字符串,你可以删除AB
或者BC
,问你是否可以将字符串删除变为空。 -
解题思路
签到,判断 c n t a + c n t c = c n t b cnt_a+cnt_c=cnt_b cnta+cntc=cntb。 -
AC代码
/**
*@filename:A_Casimir_s_String_Solitaire
*@author: pursuit
*@created: 2021-09-28 22:35
**/
#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 t;
string s;
void solve(){
}
int main(){
cin >> t;
while(t -- ){
cin >> s;
int cnt[3];
cnt[0] = cnt[1] = cnt[2] = 0;
for(int i = 0; i < s.size(); ++ i){
cnt[s[i] - 'A'] ++;
}
if(cnt[1] == cnt[0] + cnt[2])puts("YES");
else puts("NO");
}
solve();
return 0;
}
B. Shifting Sort
-
题意
给定一个数组,你每次可以选择一个区间 [ l , r ] [l,r] [l,r]然后进行循环左移 d d d。需要你将数组变成递增的操作输出,操作总数不能超过 n n n。 -
解题思路
不难发现,我们每次操作可以将一个最大值放在最右边,即我们先找到我们当前要确定的第 i i i的数,那么它肯定是在前 i i i个数中的,我们只需要找到这个数的位置 l l l,然后以它应该出现的位置 r r r,进行操作 [ l , r ] [l,r] [l,r]循环左移 1 1 1位即可。然后依次进行模拟。 -
AC代码
/**
*@filename:B_Shifting_Sort
*@author: pursuit
*@created: 2021-09-28 22:39
**/
#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 = 50 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int t, n, a[N], b[N];
struct node{
int l, r, d;
};
void solve(){
//每一次可以确定最大值。将最大值移动到相应位置即可。
vector<node> res;
int k = n;
sort(b + 1, b + 1 + n);
while(k){
int idx = 0;
for(int i = 1; i <= k; ++ i){
if(a[i] == b[k]){
idx = i;
break;
}
}
//i应该要到k。
if(idx == k){
-- k;
continue;
}
else res.push_back({idx, k, 1});
//修改ai。
for(int i = idx; i < k; ++ i)a[i] = a[i + 1];
a[k] = b[k];
-- k;
}
cout << res.size() << endl;
for(int i = 0; i < res.size(); ++ i){
cout << res[i].l << " " << res[i].r << " " << res[i].d << endl;
}
}
int main(){
cin >> t;
while(t -- ){
cin >> n;
for(int i = 1; i <= n; ++ i){
cin >> a[i];
b[i] = a[i];
}
solve();
}
return 0;
}
C. Ticks
-
题意
给你一个地图,问你是否可以进行绘制使得形成这个地图。绘制操作为选定一个点 ( i , j ) (i,j) (i,j),将其绘制成黑色,同时将左对角线和右对角线的 x x x个格子绘制成黑色,需要满足 x ≥ d x\geq d x≥d。 -
解题思路
由题可得,我们可以进行模拟,即从下往上选定一个出发点 ( i , j ) (i,j) (i,j),然后进行合理的 d f s dfs dfs判断是否可行,如果可行,将其绘制,并用vis数组标记,最后再检测黑色的格子是否都已经被标记了即可。 -
AC代码
/**
*@filename:C_Ticks
*@author: pursuit
*@created: 2021-09-28 23:22
**/
#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 = 20 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int t, n, m, k;
char s[N][N];
bool vis[N][N];//标记是否可以被染色。
int dfs1(int x, int y, bool flag, int res){
int ans = 1;
if(flag && res == 0)return ans;
if(x - 1 >= 1 && x - 1 <= n && y - 1 >= 1 && y - 1 <= m && s[x - 1][y - 1] == '*'){
ans += dfs1(x - 1, y - 1, flag, res - 1);
}
if(flag)vis[x][y] = true;
return ans;
}
int dfs2(int x, int y, bool flag, int res){
int ans = 1;
if(flag && res == 0)return ans;
if(x - 1 >= 1 && x - 1 <= n && y + 1 >= 1 && y + 1 <= m && s[x - 1][y + 1] == '*'){
ans += dfs2(x - 1, y + 1, flag, res - 1);
}
if(flag)vis[x][y] = true;
return ans;
}
void solve(){
bool flag = false;
for(int i = n; i >= 1; -- i){
for(int j = m; j >= 1; -- j){
if(s[i][j] == '*'){
int x = dfs1(i, j, false, 0) - 1, y = dfs2(i, j, false, 0) - 1;
//cout << i << " " << j << " " << x << " " << y << endl;
if(min(x, y) < k && !vis[i][j]){
flag = true;
break;
}
else if(min(x, y) >= k){
dfs1(i, j, true, min(x, y) + 1), dfs2(i, j, true, min(x, y) + 1);
}
}
}
if(flag)break;
}
for(int i = 1; i <= n; ++ i){
for(int j = 1; j <= m; ++ j){
if(s[i][j] == '*' && !vis[i][j])flag = true;
}
}
if(flag)puts("NO");
else puts("YES");
}
int main(){
cin >> t;
while(t -- ){
cin >> n >> m >> k;
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; ++ i)cin >> s[i] + 1;
solve();
}
return 0;
}
D. Productive Meeting
-
题意
给定一个数组 a a a,你每次需要选定两个数组元素都大于 0 0 0的 a [ i ] a[i] a[i]和 a [ j ] a[j] a[j],然后将它们都减 1 1 1,问操作次数最大为多少。 -
解题思路
利用优先队列或者multiset均可,我这里使用muliset每次取出实时最大值最小值进行操作,操作完之后更新即可。
ps:好像有个结论,每次取出实时最大值和次大值进行操作也是最优的,这里就可以用到优先队列,请读者自行实现。 -
AC代码
/**
*@filename:D_Productive_Meeting
*@author: pursuit
*@created: 2021-09-28 22:55
**/
#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 t, n, a[N];
struct node{
int i;
bool operator < (const node &A) const{
return a[i] < a[A.i];
}
};
void solve(){
multiset<node> s;
vector<pii> res;
for(int i = 1; i <= n; ++ i)s.insert({i});
while(s.size() > 1){
auto iter1 = s.begin(), iter2 = s.end();
-- iter2;
int x = (*iter1).i, y = (*iter2).i;
if(a[x] == 0){
s.erase(iter1);
continue;
}
if(a[y] == 0){
s.erase(iter2);
continue;
}
-- a[x], -- a[y];
s.erase(iter1), s.erase(iter2);
s.insert({x}), s.insert({y});
res.push_back({x, y});
}
cout << res.size() << endl;
for(auto iter : res){
cout << iter.first << " " << iter.second << endl;
}
}
int main(){
scanf("%d", &t);
while(t -- ){
scanf("%d", &n);
for(int i = 1; i <= n; ++ i){
scanf("%d", &a[i]);
}
solve();
}
return 0;
}
E1. Permutation Minimization by Deque
-
题意
你需要顺序将数组 p p p放入双端队列中,你需要使得放入之后的双端队列字典序最小。 -
解题思路
不难发现,双端队列的队头才是真正决定字典序大小的存在,所以我们需要使得队头最小即可。至此模拟操作。 -
AC代码
/**
*@filename:E1_Permutation_Minimization_by_Deque
*@author: pursuit
*@created: 2021-09-28 22:49
**/
#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 t, n, p[N];
void solve(){
deque<int> q;
q.push_back(p[1]);
for(int i = 2; i <= n; ++ i){
int x = q.front();
if(p[i] < x)q.push_front(p[i]);
else q.push_back(p[i]);
}
while(!q.empty()){
cout << q.front() << " ";
q.pop_front();
}
cout << endl;
}
int main(){
scanf("%d", &t);
while(t -- ){
scanf("%d", &n);
for(int i = 1; i <= n; ++ i){
scanf("%d", &p[i]);
}
solve();
}
return 0;
}
E2. Array Optimization by Deque
-
题意
你需要顺序将数组 p p p放入双端队列中,你需要使得放入之后的逆序对数量最少。 -
解题思路
由于放前面和放后面都是和队列中已有的数作比较,此次计算不会考虑队列中的顺序,所以我们放前面和放后面对之后要放的数是没有影响的,即只需要考虑放前面的代价和放后面的代价哪个小即可。实际上就是放前面的话,那么所有小于它的都要统计,放后面的话所有大于它的都要统计,这个利用树状数组实现。注意离散化。 -
AC代码
/**
*@filename:E2_Array_Optimization_by_Deque
*@author: pursuit
*@created: 2021-09-28 23:41
**/
#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 t, n, a[N];
int c[N];
vector<int> temp;
int lowbit(int x){
return x & (- x);
}
void add(int x, int v){
while(x <= n){
c[x] += v;
x += lowbit(x);
}
}
ll get(int x){
ll ans = 0;
while(x){
ans += c[x];
x -= lowbit(x);
}
return ans;
}
void solve(){
//离散化。可以记住这个离散化操作。
sort(temp.begin(), temp.end());
temp.erase(unique(temp.begin(), temp.end()), temp.end());
for(int i = 1; i <= n; ++ i){
a[i] = lower_bound(temp.begin(), temp.end(), a[i]) - temp.begin() + 1;
}
ll res = 0;
for(int i = 1; i <= n; ++ i){
res += min(get(a[i] - 1), get(n) - get(a[i]));
add(a[i], 1);
}
printf("%lld\n", res);
}
int main(){
scanf("%d", &t);
while(t -- ){
scanf("%d", &n);
temp.resize(n);
for(int i = 1; i <= n; ++ i){
scanf("%d", &a[i]);
temp[i - 1] = a[i];
c[i] = 0;
}
solve();
}
return 0;
}
F. Array Stabilization (AND version)
-
题意
给定一个数组n,下标从0开始,只包含0和1.给定d。现在需要你确定经过多少次操作后数组元素全为0。
操作为将数组循环移位 d d d,再和原数组相与得到新数组。 -
解题思路
由于与操作的特征,为 0 0 0的位置只会为 0 0 0,且其可以去将其它的位置变为 1 1 1,这种即可理解成移动,显然可以通过 b f s bfs bfs来解决,不过为了避免复杂度过大,所以我们需要减少重复的步骤(即已经放入过优先队列中的点)。至此模拟操作即可。 -
AC代码
/**
*@filename:F_Array_Stabilization_AND_version_
*@author: pursuit
*@created: 2021-09-29 17:17
**/
#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 = 1e6 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
//
int t, n, d, a[N];
pii q[N];
int st, ed;
void solve(){
st = 1, ed = 0;
int res = 0;
for(int i = 0; i < n; ++ i){
if(!a[i])q[++ ed] = {0, i};
}
while(st <= ed){
auto x = q[st ++];
int u = (x.second + d) % n;
if(a[u]){
a[u] = 0;
q[++ ed] = {x.first + 1, u};
res = max(res, x.first + 1);
}
}
for(int i = 0; i < n; ++ i){
if(a[i]){
res = -1;
break;
}
}
printf("%d\n", res);
}
int main(){
scanf("%d", &t);
while(t -- ){
scanf("%d%d", &n, &d);
for(int i = 0; i < n; ++ i){
scanf("%d", &a[i]);
}
solve();
}
return 0;
}
G. Minimal Coverage
-
题意
按顺序给定n条线段,从0开始,每一个线段可以正放也可以反放(+x,-x),但起始端必须放在上一个线段的结束位置。问n条放完之后最小覆盖区间长度。 -
解题思路
此题我们第一眼看到会发现要保存的信息太多,非常复杂,但实际上我们是可以进行一些约束的,即我们可以将左端点固定在 0 0 0处,即左端点不在 0 0 0处的我们可以进行偏移实现,这并不影响答案。
此题不然发现局部解可以构成最优解,因为我们每次都是要利用上一个结果来进行的。
所以我们用 d p [ i ] [ j ] dp[i][j] dp[i][j]表示的是前 i i i个线段,且结束位置为 j j j的线段长度。
初始状态为dp[1][a[1]], 终止状态为dp[n][j]。
注意,我们需要一直保证左端点为0,这样便于我们处理,如果不为0,我们进行偏移即可,所以数组长度需要开到2000.
状态转移
如果正放,此时不用考虑越界(即不会出现数组下标为负数),那么我们只需要枚举 d p [ i − 1 ] [ j ] dp[i - 1][j] dp[i−1][j],
则dp[i][j + a[i]] = min(dp[i][j + a[i]], max(dp[i - 1][j], j + a[i]));
注意更新线段长度,
如果反放,此时需要考虑越界,即j和a[i]的大小关系。
如果 j ≤ a [ i ] j \leq a[i] j≤a[i],那么反放会超出0,此时我们可以将其进行偏移,即区间覆盖长度相对增加了a[i] - j。
则dp[i][0] = min(dp[i][0], dp[i - 1][j] + a[i] - j)
。
否则,不会超出左边界,dp[i][j - a[i]] = min(dp[i][j - a[i]], dp[i - 1][j]);
至此题解。 -
AC代码
/**
*@filename:G_Minimal_Coverage
*@author: pursuit
*@created: 2021-09-29 20:05
**/
#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, M = 2010;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int t, n, a[N];
int dp[N][M];//dp[i][j]表示的是前i个线段,且结束位置为j的线段长度。
void solve(){
dp[1][a[1]] = a[1];
for(int i = 2; i <= n; ++ i){
for(int j = 0; j < M; ++ j){
if(j <= a[i]){
dp[i][0] = min(dp[i][0], dp[i - 1][j] + a[i] - j);
}
else{
dp[i][j - a[i]] = min(dp[i][j - a[i]], dp[i - 1][j]);
}
dp[i][j + a[i]] = min(dp[i][j + a[i]], max(dp[i - 1][j], j + a[i]));
}
}
int res = INF;
for(int j = 0; j < M; ++ j){
res = min(res, dp[n][j]);
}
printf("%d\n", res);
}
int main(){
scanf("%d", &t);
while(t -- ){
scanf("%d", &n);
for(int i = 1; i <= n; ++ i){
scanf("%d", &a[i]);
for(int j = 0; j < M; ++ j){
dp[i][j] = INF;
}
}
solve();
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)