Codeforces Round 946 (Div. 3) A~G

A.Phone Desktop (枚举)

题意:

A A A的手机有一个桌面(或称启动器)。桌面可以由多个屏幕组成。每个屏幕表示为大小为 5 × 3 5 \times 3 5×3 的网格,即五行三列。

x x x 个应用程序的图标大小为 1 × 1 1 \times 1 1×1 个单元格;这样的图标只占屏幕的一个单元格。还有 y y y 个应用程序的图标大小为 2 × 2 2 \times 2 2×2 个单元。;这样的图标在屏幕上占据了 4 4 4 个单元格的个方格。每个屏幕的每个单元格最多只能有一个图标。

A A A希望在最少的屏幕上放置应用程序图标。请帮她找出所需的最少屏幕数。

分析:

我们直接枚举需要放几个屏幕,再判断是否合法即可。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

int main() {
    int t;
    cin >> t;
    while (t--) {
        LL x, y;
        cin >> x >> y;
        for (int i = 0; i <= 200; i++) {
            if (i * 2LL < y)
                continue;
            LL ans = 7LL * i + (2LL * i - y) * 4LL;
            if (ans >= x) {
                cout << i << endl;
                break;
            }
        }
    }
    return 0;
}

B.Symmetric Encoding (模拟)

题意:

给出一个由小写字母组成的字符串 s s s 。使用以下算法对这个字符串进行编码:

  • 首先,构建一个新的辅助字符串 r r r ,该字符串由字符串 s s s 中所有不同的字母组成,按字母顺序书写;
  • 然后进行如下编码:将字符串 s s s 中的每个字符替换为字符串 r r r 中的对称字符(字符串 r r r 中的第一个字符将被最后一个字符替换,第二个字符将被从头开始的第二个字符替换,以此类推)。

例如, s s s = c o d e f o r c e s codeforces codeforces字符串的编码过程如下:

  • 字符串 r r r 得到 c d e f o r s cdefors cdefors
  • 第一个字符 s 1 s_1 s1 = c c c 被替换为 s s s
  • 第二个字符 s 2 s_2 s2 = o o o 被替换为 e e e
  • 第三个字符 s 3 s_3 s3 = d d d 被替换为 r r r
  • 最后一个字符 s 10 s_{10} s10 = s s s 被替换为 c c c
    因此,对字符串 s s s = c o d e f o r c e s codeforces codeforces 进行编码的结果是字符串 s e r o f e d s o c serofedsoc serofedsoc
    编写一个程序来执行解码,即从编码结果中还原出原始字符串 s s s

分析:

我们首先对字符串去重,再用 m a p map map存储映射关系,按照题意模拟即可。

代码:

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

int main() {
    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        string a;
        cin >> a;
        map<char, int> mp;
        vector<char> tmp;
        for (auto t: a) {
            if (!mp[t]) {
                mp[t] = 1, tmp.push_back(t);
            }
        }
        sort(tmp.begin(), tmp.end());
        for (int i = 0; i < tmp.size(); i++) mp[tmp[i]] = i;
        for (auto &t: a) cout << tmp[tmp.size() - mp[t] - 1];
        cout << endl;
    }
    return 0;
}

C. Beautiful Triple Pairs (数学)

题意:

给出由 n n n个整数组成的数组 a a a。每个 j j j ( 1 ≤ j ≤ n − 2 1 \le j \le n - 2 1jn2 ) 都有一个由 [ a j , a j + 1 , a j + 2 ] [a_j, a_{j + 1}, a_{j + 2}] [aj,aj+1,aj+2] 组成的三元组。

如果一对三元组 b b b c c c 在一个位置上完全不同,即满足以下条件之一,那么它们就是美丽的:

  • b 1 ≠ c 1 b_1 \ne c_1 b1=c1 b 2 = c 2 b_2 = c_2 b2=c2 b 3 = c 3 b_3 = c_3 b3=c3
  • b 1 = c 1 b_1 = c_1 b1=c1 b 2 ≠ c 2 b_2 \ne c_2 b2=c2 b 3 = c 3 b_3 = c_3 b3=c3
  • b 1 = c 1 b_1 = c_1 b1=c1 b 2 = c 2 b_2 = c_2 b2=c2 b 3 ≠ c 3 b_3 \ne c_3 b3=c3

求写成的三元组 [ a j , a j + 1 , a j + 2 ] [a_j, a_{j + 1}, a_{j + 2}] [aj,aj+1,aj+2] 中优美的三元组对数。

分析:

假设三元组为 ( x , y , z ) (x,y,z) (x,y,z),当保证 x x x不同时, y , z y,z y,z相同时,结果为 [ ? , y , z ] [?, y, z] [?,y,z]的数量减去 [ x , y , z ] [x, y, z] [x,y,z]的数量。另外两种情况类似,所以我们只要用计算出三种情况的数量再减去 3 3 3倍的[x, y, z]$的数量即可。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

int main() {
    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        vector<int> a(n + 1);
        for (int i = 1; i <= n; i++)
            cin >> a[i];
        map<array<int, 3>, LL> mp;
        map<array<int, 2>, LL> mp1, mp2, mp3;
        LL ans = 0;
        for (int i = 3; i <= n; i++) {
            ans += mp1[{a[i - 2], a[i - 1]}];
            ans += mp2[{a[i - 2], a[i]}];
            ans += mp3[{a[i - 1], a[i]}];
            ans -= 3ll * mp[{a[i - 2], a[i - 1], a[i]}];
            mp1[{a[i - 2], a[i - 1]}]++;
            mp2[{a[i - 2], a[i]}]++;
            mp3[{a[i - 1], a[i]}]++;
            mp[{a[i - 2], a[i - 1], a[i]}]++;
        }
        cout << ans << endl;
    }
    return 0;
}

D.Ingenuity-2 (思维)

题意:

我们把火星表面想象成一个无限坐标平面。最初,"毅力-2 号 "漫游车和 "智慧-2 号 "直升机位于坐标为 ( 0 , 0 ) (0, 0) (0,0) 的点上。现在为它们专门开发了一套指令 s s s ,由以下类型的 n n n 指令组成:

  • N N N:向北移动一米(从点 ( x , y ) (x, y) (x,y) ( x , y + 1 ) (x, y + 1) (x,y+1) );
  • S S S:向南移动一米(从点 ( x , y ) (x, y) (x,y) ( x , y − 1 ) (x, y - 1) (x,y1) );
  • E E E:向东移动一米(从点 ( x , y ) (x, y) (x,y) 移至点 ( x + 1 , y ) (x + 1, y) (x+1,y) );
  • W W W:向西移动一米(从点 ( x , y ) (x, y) (x,y) 移至 ( x − 1 , y ) (x - 1, y) (x1,y) )。

每条指令必须由漫游车或直升机执行。此外,漫游车或直升机都必须至少执行一条指令。你的任务是分配指令,使直升机和漫游者在执行所有 n n n条指令后,最终到达相同的点,或者确定这是不可能的。

分析:

我们首先考虑不可能的情况:

  • n n n为奇数
  • N N N S S S E E E W W W的奇偶性不一致

  • n = 2 n=2 n=2,但两次行动不相同

除掉上述情况,我们可以按照以下顺序执行目标:
N N N S S S先分配给直升机,再分配给探测车。 E E E W W W的分配顺序则相反,这样我们可以保证符合题意。

代码:

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

int main() {
    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        vector<char> a(n);
        map<char, int> m;
        for (auto &t: a) {
            cin >> t;
            m[t]++;
        }
        if (n % 2 || m['N'] % 2 != m['S'] % 2 || m['E'] % 2 != m['W'] % 2) {
            cout << "NO" << endl;
            continue;
        }
        if (n == 2 && a[0] != a[1]) {
            cout << "NO" << endl;
            continue;
        }
        vector<char> ans(n);
        for (auto tmp: {'N', 'S', 'E', 'W'}) {
            int flag = 0;
            if (tmp == 'N' || tmp == 'S') flag = 1;
            for (int i = 0; i < n; i++) {
                char it = a[i];
                if (it == tmp) {
                    if (flag) {
                        ans[i] = 'R';
                    } else {
                        ans[i] = 'H';
                    }
                    flag ^= 1;
                }
            }
        }
        for (auto &tmp: ans) cout << tmp;
        cout << endl;
    }
    return 0;
}

E.Money Buys Happiness(动态规划)

题意:

作为一名物理学家,查理喜欢用简单精确的语言来规划自己的生活。

在接下来的 m m m个月里,从没有钱开始,查理将努力工作,每月赚取 x x x英镑。在第 i i i ( 1 ≤ i ≤ m ) (1\le i \le m) (1im)里,将有一次机会,付出 c i c_i ci英镑的代价,获得幸福 h i h_i hi

不允许借贷。在第 i i i月赚到的钱只能在之后的第 j j j月( j > i j\gt i j>i)花掉。

既然物理学家不会编程,帮查理找出幸福的最大值吧。

分析:

从数据范围可以得知天数和可以得到的所有的幸福值数据范围很小,考虑 d p dp dp

状态 i i i表示第几天,状态 j j j表示幸福值。

d p i j dp_{ij} dpij代表 i i i天获得 j j j点幸福值所剩的最多钱。

每次把这个月剩的钱数和这个月新赚的的钱数加到下一个月。 如果当前的钱数可以购买今天的幸福值的时候,购买之后下个月对应的幸福度所剩的钱数和和我当前的钱数+本月的钱数-买本月幸福度所花费的钱取个最大值即可。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;
const LL N = 2e5 + 10;
const LL MIN = -1e18;
LL a[N], b[N];
LL dp[100][N];

void solve() {
    LL m, x;
    cin >> m >> x;
    LL sum = 0;
    for (LL i = 1; i <= m; i++) {
        cin >> a[i] >> b[i];
        sum += b[i];
    }
    for (LL i = 0; i <= m + 1; i++) {
        for (LL j = 0; j <= sum; j++) {
            dp[i][j] = MIN;
        }
    }
    dp[1][0] = 0;
    for (LL i = 1; i <= m; i++) {
        for (LL j = 0; j <= sum; j++)
            dp[i + 1][j] = dp[i][j] + x;
        for (LL j = 0; j <= sum; j++) {
            if (dp[i][j] >= a[i]) {
                dp[i + 1][j + b[i]] = max(dp[i + 1][j + b[i]], dp[i][j] + x - a[i]);
            }
        }
    }
    for (LL i = sum; i >= 0; i--) {
        if (dp[m + 1][i] >= 0) {
            cout << i << endl;
            return;
        }
    }
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

F.Cutting Game(模拟、数据结构)

题意:

爱丽丝和鲍勃又在玩一个游戏。他们有一个大小为 a × b a\times b a×b( 1 ≤ a , b ≤ 1 0 9 1\le a,b\le 10^9 1a,b109)的网格,网格上有 n n n个筹码,每个单元格中最多有一个筹码。位于第 x x x行和第 y y y列交点的单元格坐标为 ( x , y ) (x, y) (x,y)

爱丽丝先下一步棋,然后棋手们轮流下棋。在每一步棋中,棋手可以从剩余网格的开头或末尾切掉几行或几列(但不是全部),并为网格切掉部分的每一个筹码赢得一分。每一步棋都可以用字符"U"、“D”、"L"或"R"以及整数 k k k来描述:

  • 如果字符为"U",那么将剪切剩余的前 k k k行;
  • 如果字符为"D",则将剪切剩余的最后 k k k行;
  • 如果字符为"L",则将剪切剩余的前 k k k列;
  • 如果字符为"R",则将剪切剩余的最后 k k k列。

根据网格的初始状态和玩家的移动,确定爱丽丝和鲍勃分别获得的点数。

分析:

以横坐标正序,横坐标倒序,纵坐标正序和纵坐标倒序分别对于所有点建立优先队列。

对于询问,这里以"U"操作为例,首先可以计算出新的矩形上边界,然后就可以在优先队列中得到横坐标在被截取范围内的所有点。然后,只需要判断该点的纵坐标是否在当前未被截去的纵坐标范围内即可。

实现的时候用 m a p map map维护每个点是否被取走,实现方式是类似的。因为每个点只会被取到四次,总时间复杂度为
O ( n l o g n ) O(nlogn) O(nlogn)

代码:

#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
map<pair<LL,LL>,LL>mp;
void solve(){
    mp.clear();
    LL u,d,l,r,a,b,n,m;
    cin>>a>>b>>n>>m;
    u=1,d=a,l=1,r=b;
    priority_queue<pair<LL,LL>>q1,q2,q3,q4;
    for(LL i=1;i<=n;++i){
        LL x,y;cin>>x>>y;mp[make_pair(x,y)]=1;
        q1.push(make_pair(x,y));
        q2.push(make_pair(-x,y));
        q3.push(make_pair(y,x));
        q4.push(make_pair(-y,x));
    }
    LL ans[2]={};
    for(LL i=1;i<=m;++i){
        char op;
        LL t;
        cin>>op>>t;
        if(op=='U'){
            u+=t;
            while(!q2.empty()){
                auto U=q2.top();
                if(-U.first>=u)
                    break;
                if(mp[make_pair(-U.first,U.second)]){
                    mp[make_pair(-U.first,U.second)]=0;
                    ++ans[i%2];
                }
                q2.pop();
            }
        }
        if(op=='D'){
            d-=t;
            while(!q1.empty()){
                auto u=q1.top();
                if(u.first<=d)
                    break;
                if(mp[u]){
                    mp[u]=0;
                    ++ans[i%2];
                }
                q1.pop();
            }
        }
        if(op=='L'){
            l+=t;
            while(!q4.empty()){
                auto u=q4.top();
                if(-u.first>=l)
                    break;
                if(mp[make_pair(u.second,-u.first)]){
                    mp[make_pair(u.second,-u.first)]=0;
                    ++ans[i%2];
                }
                q4.pop();
            }
        }
        if(op=='R'){
            r-=t;
            while(!q3.empty()){
                auto u=q3.top();
                if(u.first<=r)
                    break;
                if(mp[make_pair(u.second,u.first)]){
                    mp[make_pair(u.second,u.first)]=0;
                    ++ans[i%2];
                }
                q3.pop();
            }
        }
    }
    cout<<ans[1]<<' '<<ans[0]<<endl;
}

int main(){
    LL t;
    cin>>t;
    while(t--)
        solve();
    return 0;
}

G.Money Buys Less Happiness Now(数据结构)

题意:

在这个版本的题目中,每个月只能购买 h i = 1 h_i=1 hi=1幸福,但月数却大大增加了。我们进入了量子幸福和时间膨胀的领域。

作为一名物理学家,查理喜欢用简单精确的语言来规划自己的生活。

在接下来的 m m m个月里,查理将从没有钱开始,努力工作,每月赚取 x x x英镑。在第 i i i个月 ( 1 ≤ i ≤ m ) (1\le i \le m) (1im)里,只有一次机会,付出 c i c_i ci英镑的代价来获得一个单位的幸福。每月不能购买超过一个单位。

不允许借贷。在第 i i i月赚到的钱只能在之后的第 j j j月花掉( j > i j\gt i j>i)。

帮查理找出可达到的最大幸福值吧。

分析:

本题用优先队列维护即可,每次如果当前费用足够直接购买,否则查看先前购买的最贵的是不是比当前的还贵,如果是的就不要前面那个,选择当前的幸福感,否则就放弃当前的物品。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;

void solve() {
    LL n, x, sum = 0;
    cin >> n >> x;
    priority_queue<LL> q;
    for (int i = 1; i <= n; i++, sum += x) {
        LL tmp;
        cin >> tmp;
        if (sum >= tmp) {
            q.push(tmp);
            sum -= tmp;
        } else if (q.size()) {
            LL pre = q.top();
            q.pop();
            if (pre > tmp) {
                sum += pre;
                sum -= tmp;
                q.push(tmp);
            } else
                q.push(pre);
        }
    }
    cout << q.size() << endl;
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

赛后交流

在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。

群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值