首页 > 其他分享 >Educational Codeforces Round 150 (Rated for Div. 2) A-E

Educational Codeforces Round 150 (Rated for Div. 2) A-E

时间:2023-06-25 22:24:13浏览次数:48  
标签:150 Educational Rated int 线段 long solve using ll

比赛链接

A

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

bool solve() {
    int n;
    cin >> n;
    if (n <= 4) cout << "Bob" << '\n';
    else cout << "Alice" << '\n';
    return true;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        if (!solve()) cout << -1 << '\n';
    }
    return 0;
}

B

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

bool solve() {
    int q;
    cin >> q;

    bool ok = 1;
    int limit = -1;
    int pre = 0;
    while (q--) {
        int x;
        cin >> x;
        if (limit < 0) limit = x;
        if (ok) {
            if (pre <= x || x <= limit) {
                if (pre > x) ok = 0;
                cout << 1;
                pre = x;
            }
            else cout << 0;
        }
        else {
            if (pre <= x && x <= limit) cout << 1, pre = x;
            else cout << 0;
        }
    }
    cout << '\n';
    return true;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        if (!solve()) cout << -1 << '\n';
    }
    return 0;
}

C

题目

给定一个字符串 \(s\) ,仅含有 A-E \(5\) 个大写字母,其中字母代表一个值 |A| = 1, ..., |E| = 10000

一个字符串的值为 \(\displaystyle \sum_{i=1}^n (-1)^{\left[s_i < \max\limits_{i+1 \leq j \leq n}(s_i)\right]} \cdot |s_i|\) 。

题解

方法一

知识点:线性dp,枚举。

考虑枚举每个位置作为修改的位置。

对于位置 \(i\) 的修改,只会影响到 \([1,i-1]\) 之间字符值的符号,而 \([i+1,n]\) 是不会被影响的。因此,我们需要预先知道, \([1,i-1]\) 在 \([i,n]\) 最大值确定时的值。

设 \(f_{i,j}\) 表示 \([1,i-1]\) 在 \([i,n]\) 的最大字符为 \(j\) 时的值。转移方程为:

\[f_{i,j} = f_{i-1,\max(s_{i-1},j)} + (-1)^{[s_{i-1}<j]} \cdot |s_{i-1}| \]

随后,我们从后往前枚举修改位置 \(i\) 方便递推。设 \([i+1,n]\) 部分的值 \(sub\) 以及最大字符 \(mx\) ,若 \(s_i\) 修改为 \(j\) ,答案则为:

\[f_{i,\max(mx, j)} + (-1)^{[j < mx]} \cdot |j| + sub \]

然后,我们递推 \(sub = sub + (-1)^{[s_i < mx]} \cdot |s_i|\) ,然后 \(mx = \max(mx,s_i)\) 。

最后,取 \(n\) 个修改位置的最大值即可。

时间复杂度 \(O(n)\)

空间复杂度 \(O(n)\)

方法二

知识点:线性dp。

由于修改 \(i\) 时,\([i+1,n]\) 是不会被影响的,考虑从右往左递推。

设 \(f_{i,j,k}\) 表示 \([i,n]\) 的最大字符是 \(j\) 且修改了 \(k\) 次的最大值。转移方程为:

\[f_{i,\max(j,y),k + [s_{i} \neq y]} = f_{i+1,j,k} + (-1)^{[y<\max(j,y)]} \cdot |y| \]

其中 \(y\) 是 \(s_{i}\) 的字符, \(k+[s_{i} \neq y]\) 应该小于 \(2\) 保证修改次数小于 \(2\) 。

这种方法比方法一更加通用好写。

时间复杂度 \(O(n)\)

空间复杂度 \(O(n)\)

代码

方法一

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

int f[200007][5];//[1,i-1]在[i,n]的最大值为j时的值
const int mp[5] = { 1,10,100,1000,10000 };
bool solve() {
    string s;
    cin >> s;
    int n = s.size();
    s = "?" + s;

    for (int i = 2;i <= n;i++) {
        int x = s[i - 1] - 'A';
        for (int j = 0;j < 5;j++) f[i][j] = f[i - 1][max(j, x)] + (x < j ? -1 : 1) * mp[x];
    }

    int ans = -2e9;
    int sub = 0, mx = 0;
    for (int i = n;i >= 1;i--) {
        for (int j = 0;j < 5;j++) ans = max(ans, f[i][max(mx, j)] + (j < mx ? -1 : 1) * mp[j] + sub);
        int x = s[i] - 'A';
        sub += (x < mx ? -1 : 1) * mp[x];
        mx = max(x, mx);
    }

    cout << ans << '\n';
    return true;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        if (!solve()) cout << -1 << '\n';
    }
    return 0;
}

方法二

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

int f[2][5][2];//[i,n]的最大值为j且修改了k次时的值
const int mp[5] = { 1,10,100,1000,10000 };
bool solve() {
    string s;
    cin >> s;
    int n = s.size();
    s = "?" + s;

    for (int j = 0;j < 5;j++) f[0][j][0] = f[0][j][1] = -1e9;
    f[0][0][0] = 0;
    for (int i = n;i >= 1;i--) {
        for (int j = 0;j < 5;j++) f[1][j][0] = f[1][j][1] = -1e9;

        int x = s[i] - 'A';
        for (int j = 0;j < 5;j++) {
            for (int k = 0;k < 2;k++) {
                for (int y = 0;y < 5;y++) {
                    int jj = max(j, y);
                    int kk = k + (x != y); // k = 1,x != y,之前修改了这次也修改了,所以不合法
                    if (kk < 2) f[1][jj][kk] = max(f[1][jj][kk], f[0][j][k] + (y < jj ? -1 : 1) * mp[y]);
                }
            }
        }

        swap(f[0], f[1]);
    }

    int ans = -1e9;
    for (int j = 0;j < 5;j++) ans = max({ ans,f[0][j][0],f[0][j][1] });
    cout << ans << '\n';
    return true;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        if (!solve()) cout << -1 << '\n';
    }
    return 0;
}

D

题目

有 \(k\) 个线段, \(k\) 是偶数,如果能把他们分成两个一组,且满足要求:

  1. 每个线段只能出现在一个组里。
  2. 同一组的线段必须相交。
  3. 不同组的线段不能相交。

那么称这 \(k\) 个线段是合格的。

现在给你 \(n\) 个线段,问最少需要去掉多少条线段,才能使得剩下的线段是合格的。

题解

知识点:贪心,枚举。

问题等价于最多能分多少组,就是活动安排问题的升级版,思路还是一样的。

先按右端点排序,贪心地从左往右选,是为了尽可能让后面选的线段不会与前面已经分好的组碰撞。我们用 \(maxr\) 记录已经分好组的线段的最大右端点,之后左端点小于等于它的都是不能选的。

我们还需要让同一组的线段相交,所以需要记录上一个选好的线段的右端点 \(lastr\) 。新的线段在大于 \(maxr\) 的基础上,若小于等于 \(lastr\) ,那么就可以与上一个选的线段直接贪心地合并成一组,这样保证新的组右端点最小,同样是为了尽可能让后面选的线段不会与前面已经分好的组碰撞,然后答案加 \(1\) 并且更新 \(maxr\) ;否则,用新的线段的右端点更新 \(lastr\) ,使得后面更有机会和这个线段合并。

时间复杂度 \(O(n \log n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

int l[2007], r[2007];
int ord[2007];

bool solve() {
    int n;
    cin >> n;
    for (int i = 1;i <= n;i++) cin >> l[i] >> r[i];
    iota(ord + 1, ord + n + 1, 1);
    sort(ord + 1, ord + n + 1, [&](int a, int b) {return r[a] < r[b];});

    int ans = 0, maxr = -1, lastr = -1;
    for (int i = 1;i <= n;i++) {
        int id = ord[i];
        if (l[id] <= maxr) continue;
        if (l[id] <= lastr) {
            ans++;
            maxr = r[id];
        }
        else lastr = r[id];
    }
    cout << n - 2 * ans << '\n';
    return true;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        if (!solve()) cout << -1 << '\n';
    }
    return 0;
}

E

题目

给一个 \(n\times n\) 的矩阵,第 \(i\) 列 \([1,a_i]\) 的格子是黑的而 \([a_i+1,n]\) 的格子是白的。

有 \(1, \cdots ,m\) 的 \(m\) 个数字,要填在矩阵中,每个数字只能填一次,只能填在白色的格子里。

定义一个矩阵的值为数字 \(j\) 的数量,其中 \(j\) 满足 \(j+1\) 填在同一行下一列。

求矩阵的最大值。

题解

方法一

知识点:单调栈,贪心,枚举。

模拟一下发现,每行白色格子被分成了若干段,要将数字填在尽量长的白色连续段内,因为一个白色连续段只会损失一个数字。因此,问题变成如何求出各种长度的白色连续段有多少个。

对于一个白色连续段的长度,其实就是它所在的位置能左右扩展直到遇到黑色格子的长度,即左右第一个白色列高小于自己的列高的位置。同时,我们发现一组相同位置和长度,但高度不同的连续白色段,一定是上下相邻的,且最大高度一定是跨越的白色列的高度最小值,最小高度一定是左右边界高度的最大值。

因此,考虑求出每个白色列以自己最大高度能扩展的左右边界,就是以这列高度为最大高度的白色连续段边界。这是个经典问题,可以用单调栈解决左右扩展边界的问题。

随后,我们就可以通过每个白色列的左右边界求出白色连续段的长度,列高减去左右边界的最大高度就是以这一列高度为最大高度往下的连续白色段的行数,即段数。

为了防止相同高度的白色列重复计算同一组白色连续段,考虑将左边界的意义改为第一个白色列高小于等于自己的列。这样只有最左侧的一列才会计算一次,其他列会因为左边界的高度等于自己的高度,从而计算出的段数为 \(0\) 。

得到了各种长度的白色连续段数,就可以贪心填数字了,注意数字不够填可以填段的部分。

时间复杂度 \(O(n)\)

空间复杂度 \(O(n)\)

方法二

知识点:STL,贪心,枚举。

注意到,白色连续段是从低到高,即行从大到小,逐渐被黑色格子分段的。

我们可以用 set 模拟分割过程,从低到高加入分段点,找到被分段的白色连续段端点可以计算出长度,再利用当前高度减去左右边界的最大高度,即左右边界的行最小值,就可以求出段数。

时间复杂度 \(O(n \log n)\)

空间复杂度 \(O(n)\)

代码

方法一

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

int a[200007];
int l[200007], r[200007];
ll cnt[200007];
bool solve() {
    int n;
    cin >> n;
    for (int i = 1;i <= n;i++) {
        cin >> a[i];
        a[i] = n - a[i];
        l[i] = 0, r[i] = n + 1;
        cnt[i] = 0;
    }
    ll m;
    cin >> m;

    vector<int> stk;
    for (int i = 1;i <= n;i++) {
        while (stk.size() && a[stk.back()] > a[i]) {
            r[stk.back()] = i;
            stk.pop_back();
        }// 右开边界是第一个小于
        if (stk.size()) l[i] = stk.back();// 左开边界是第一个小于等于
        stk.push_back(i);
    }
    //! 左侧第一个小于等于,会让左侧第一个相同高度的有正确的左边界,其他的会在左侧等于的地方停止,避免之后重复计数
    //! 右侧第一个小于,是因为要让左侧第一个相同高度有正确的右边界,其他的虽然也会到正确的右边界,但是会因为左边界高度被排除
    //! 也可以左侧小于,右侧小于等于

    for (int i = 1;i <= n;i++) {
        int len = r[i] - l[i] - 1;
        int h = 0;
        if (l[i] >= 1) h = max(h, a[l[i]]);//! 相同的高度,但不是左侧第一个,h会等于a[i]
        if (r[i] <= n) h = max(h, a[r[i]]);
        cnt[len] += a[i] - h;
    }

    ll rest = m;
    ll ans = m;
    for (int i = n;i >= 1;i--) {
        if (i * cnt[i] < rest) {
            ans -= cnt[i];
            rest -= cnt[i] * i;
        }
        else {
            ans -= (rest + i - 1) / i;
            rest = 0;
        }
    }
    cout << ans << '\n';
    return true;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        if (!solve()) cout << -1 << '\n';
    }
    return 0;
}

方法二

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

int a[200007];
vector<int> pos[200007];
ll cnt[200007];
bool solve() {
    int n;
    cin >> n;
    for (int i = 0;i <= n;i++) pos[i].clear();
    for (int i = 1;i <= n;i++) {
        cin >> a[i];
        pos[a[i]].push_back(i);
        cnt[i] = 0;
    }
    ll m;
    cin >> m;

    set<int> st;
    st.insert(0);
    st.insert(n + 1);
    a[0] = a[n + 1] = n;
    for (int i = n;i >= 0;i--) {
        for (auto x : pos[i]) {
            auto it = st.lower_bound(x);
            int pre = *prev(it);
            int nxt = *it;
            cnt[nxt - pre - 1] += min(a[pre], a[nxt]) - i;
            st.insert(x);
        }
    }

    ll rest = m;
    ll ans = m;
    for (int i = n;i >= 1;i--) {
        if (i * cnt[i] < rest) {
            ans -= cnt[i];
            rest -= cnt[i] * i;
        }
        else {
            ans -= (rest + i - 1) / i;
            rest = 0;
        }
    }
    cout << ans << '\n';
    return true;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        if (!solve()) cout << -1 << '\n';
    }
    return 0;
}

标签:150,Educational,Rated,int,线段,long,solve,using,ll
From: https://www.cnblogs.com/BlankYang/p/17504123.html

相关文章

  • ASM disk group mount fails with ORA-15036: disk is truncated [ID 1077175.1]
     ASMdiskgroupmountfailswithORA-15036:diskistruncated[ID1077175.1]--------------------------------------------------------------------------------  修改时间05-OCT-2011    类型PROBLEM    状态PUBLISHED  InthisDocument Sympto......
  • ORA-15061 reported while doing a file operation with 11.1 or 11.2 ASM after PSU
    ORA-15061reportedwhiledoingafileoperationwith11.1or11.2ASMafterPSUappliedindatabasehome[ID1070880.1]--------------------------------------------------------------------------------修改时间26-OCT-2011类型PROBLEM状态PUBLISH......
  • 添加ASM磁盘报错ORA-02097和ORA-15014
    添加ASM磁盘报错ORA-02097和ORA-15014背景:   这是一套正在安装的11.2.0.1RAC,GridInfrastructure已经安装完成,ASMLib和磁盘分区均已完成,在通过asmca图形界面创建磁盘的时候没有发现成员盘。问题现象:问题分析:从报错信息上来看可以很明显的看出是因为参数asm_diskstring参......
  • Automatic quality of generated text Evaluation for Large Language Models,针对大模
    一、LLM生成结果自动化评测的技术挑战和研发背景LargeLanguageModels(LLMs)haverecentlygrownrapidlyandtheyhavethepotentialtoleadtheAItransformation.ItiscriticaltoevaluateLLMsaccuratelybecause: Highqualityrequirementsforgenerativere......
  • 论文阅读 | Soteria: Provable Defense against Privacy Leakage in Federated Learni
    Soteria:基于表示的联邦学习中可证明的隐私泄露防御https://ieeexplore.ieee.org/document/95781923FL隐私泄露的根本原因3.1FL中的表示层信息泄露问题设置在FL中,有多个设备和一个中央服务器。服务器协调FL进程,其中每个参与设备仅与服务器通信本地模型参数,同时保持其本地......
  • LabVIEW与西门子S7系列/三菱全系列/欧姆龙PLC通讯 支持西门子S7系列S7-1200,S7-300,S7-1
    LabVIEW与西门子S7系列/三菱全系列/欧姆龙PLC通讯支持西门子S7系列S7-1200,S7-300,S7-1500,S7-200SMART直接TCP访问IO输入输出和M,DB,V等等寄存器支持三菱FX,Q系列FX2N,FX3U,FX5U,Q系列直接TCP访问XY输入输出和M,D等等寄存器支持欧姆龙全系列直接TCP访问输入输出和M,D等等寄存器ID:6445607......
  • Java面试题集(136-150)
    Java程序员面试题集(136-150)摘要:这一部分主要是数据结构和算法相关的面试题目,虽然只有15道题目,但是包含的信息量还是很大的,很多题目背后的解题思路和算法是非常值得玩味的。136、给出下面的二叉树先序、中序、后序遍历的序列?答:先序序列:ABDEGHCF;中序序列:DBGEHACF;后序序列:DGHEBFCA。补......
  • Educational Codeforces Round 82 (Rated for Div. 2)_A. Erasing Zeroes(C++_模拟)
    Youaregivenastring.Eachcharacteriseither0or1.Youwantall1’sinthestringtoformacontiguoussubsegment.Forexample,ifthestringis0,1,00111or01111100,thenall1’sformacontiguoussubsegment,andifthestringis0101,100001o......
  • 题解:【AT Educational DP Contest-O】 Matching
    题目链接来点位运算优化,目前也是拿下了洛谷最优解,比第二名快一倍:#include<bits/stdc++.h>#defineintlonglong#definebtp(x)__builtin_popcount(x)#definelowbit(x)((x)&(-x))usingnamespacestd;namespaceFastIO{template<typenameT=int>inlineTread()......
  • 代码随想录算法训练营第十天| 20. 有效的括号 1047. 删除字符串中的所有相邻重复项
    20.有效的括号  特点:左括号之后,可能还会有左括号,但是只要有右括号,那么它必须立刻和最近的左括号代码:1charreturnRightChar(char&c)2{3switch(c)4{5case'[':return']';6case'(':return')';7case'{':r......