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 xd

  • 解题思路
    由题可得,我们可以进行模拟,即从下往上选定一个出发点 ( 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[i1][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] ja[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;
}
posted @   unique_pursuit  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示