Educational Codeforces Round 137 (Rated for Div. 2) DEF

Educational Codeforces Round 137 (Rated for Div. 2)

D - Problem with Random Tests

prob. :

给一个01串s,找到s的两个子串,使得这两个串在右对齐的情况下按位或的值最大,输出去除前导零的或运算后的最大值

数据随机生成,n 1e6

一些刚开始看成异或害搁那造tire树我真是服了自己

ideas:

贪心一下,其中一个串肯定是从左往右第一个1开始的后缀,它可以贡献最高位的1

另一个串首先需要把从左往右第一个0给变成1,也就是说它最高位是1,同时他的长度要足够能够到这个0的位置

记原串中第一个1的位置为pos1,第一个0的位置为pos0,

第二个串的选取因此会变成在pos0与pos1之间

因为数据随机生成这样的串不多,直接暴力

btw,如果整1e6次 string tmp = tmp + ‘0’ 这种会T(

code:

#include "bits/stdc++.h"

using namespace std;

const int N = 1e6 + 10;

char s[N];

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

    int n;
    cin >> n;
    cin >> (s + 1);

    int pos1 = -1, pos0 = -1;
    for (int i = 1; i <= n; ++i) {
        if (pos1 == -1 && s[i] == '1') pos1 = i;
        if (pos1 != -1 && pos0 == -1 && s[i] == '0') pos0 = i;
    }

    if (pos1 == -1) {
        cout << 0;
        return 0;
    }

    if (pos0 == -1) {
        for (int i = pos1; i <= n; ++i) cout << s[i];
        return 0;
    }

    int len = n - pos0 + 1;

    vector<char> res(len);
    for (int i = pos0; i <= n; ++i) res[i - pos0] = s[i];

    for (int i = 1; i + len - 1 <= n; ++i) {
        if (s[i] == '0') continue;
        bool flag = false;
        for (int j = 0; j < len; ++j) {
            char ch;
            if (s[pos0 + j] == '1' || s[i + j] == '1') ch = '1';
            else ch = '0';
            if (flag) {
                res[j] = ch;
                continue;
            }
            if (ch < res[j]) break;
            if (ch > res[j]) {
                flag = true;
                res[j] = ch;
            }
        }
    }

    for (int i = pos1; i < pos0; ++i) cout << s[i];
    for (auto x : res) cout << x;
}

E - FTL

prob. :

有两个技能,每个技能都有一个伤害值 p i p_i pi和一个冷却时间 t i t_i ti,目标有一个血量 h h h和一个防御值 s s s,每次可以选择一个技能发射会造成 p i − s p_i-s pis的伤害,或者两个技能一起发射造成 p 0 + p 1 − s p_0+p_1-s p0+p1s的伤害,问最少多少时间能将目标击败

p 5000, t 1e12 ,h 5000,s min{p0,p1}

ideas:

刚开始尝试贪心然后发现大分讨而且贪不出来

考虑dp

状态描述,由于t很大无法描述状态,又发现伤害和技能释放次数都是5000的量级

观察题目发现在一些时间两个技能可以看作独立,但不可能完全独立,会有一些时候是一起释放的,从这里入手,使得所有记录的状态两个技能都是刚释放完

一起释放技能的最优的情况也很难讨论清楚。设从上一次一起释放到下一次一起释放的时间T,在T之前两个技能都是各自释放,然后一个技能等待另一个技能冷却。容易想到T是t0或t1的倍数。考虑在已知T的条件下怎么完善这个问题,先放一放。

击败目标的过程是在某次一起释放后,两个技能独立不停地释放

最后这段独立释放技能的时间T‘也是t0或t1的倍数

d p [ i ] dp[i] dp[i] 表示最后一次是一起释放技能时,造成i点伤害的最少时间

转移的时候考虑枚举T(最多是h倍的t0或t1)

实际上,以0号技能为主时,每次枚举之前两个技能一起释放后已造成的伤害i,0号技能的释放次数j,可以得出从i之后两个技能都是独立不停地释放会造成的伤害(用来更新最终答案),以及如果此时可以构成两个技能一起释放的条件,更新下一个dp值

讲得可能还是有点乱,不明白可以结合官方的英文题解或code食用

code:

#include "bits/stdc++.h"

using namespace std;

typedef long long ll;

const ll N = 5010;
const ll inf = 0x3f3f3f3f3f3f3f3f;

ll p[2], t[2];
ll h, s;
ll dp[N];

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

    cin >> p[0] >> t[0] >> p[1] >> t[1] >> h >> s;

    memset(dp, 0x3f, sizeof dp);
    dp[0] = 0;

    ll ans = inf;

    for (ll i = 0; i <= h; ++i) {
        for (ll j = 1; j + i <= h; ++j) {
            for (ll k = 0; k < 2; ++k) {
                ll tot = i + j * (p[k] - s) + (j * t[k] / t[1 - k]) * (p[1 - k] - s);
                if (tot >= h) {
                    ans = min(ans, dp[i] + j * t[k]);
                }
                if (j * t[k] >= t[1 - k]) {
                    ll tmp = i + (j - 1) * (p[k] - s) + (j * t[k] / t[1 - k] - 1) * (p[1 - k] - s)
                             + (p[0] + p[1] - s);
                    if (tmp > h) tmp = h;
                    dp[tmp] = min(dp[tmp], dp[i] + j * t[k]);
                }
            }
        }
    }

    ans = min(ans, dp[h]);

    cout << ans << endl;
}

F - Intersection and Union

prob:

给n个区间,每个区间相当于一个包含[l, r] 的数的集合,给出三种运算

∪,集合求并;⊕,集合的并减去集合的交;∩,集合求交;

∑ ∣ ( ( ( S 1   o p 1   S 2 )   o p 2   S 3 )   o p 3   S 4 ) … o p n − 1 S n ∣ \sum|(((S_1 \,op_1\,S2)\,op_2 \,S3) \, op3\, S4)\dots op_{n-1}S_n| (((S1op1S2)op2S3)op3S4)opn1Sn, 其中每个op是上述三种之一,求的是一共 3 n − 1 3^{n - 1} 3n1种可能结果的和

ideas1:

组合数+并查集

这个想法挺妙的自己再记录一下

参考知乎某题解:https://zhuanlan.zhihu.com/p/574586694

观察发现这个集合运算过程不满足结合律和交换律

考虑拆开计算贡献

考虑对于每个数字x在最后答案里的贡献是多少

对于每个数字x,如果被第i个区间包含,在i个区间之前的运算结果里如果有x,为了在该区间参与运算后保留x,op可以是∪或∩;如果在之前的结果里没有x,op可以是∪或⊕,都是3种里面取2种

将每个运算符和后面的区间放在一起

对于上述i我们只要取x最后一次出现的区间位置lst,对于lst之前op随意,即每个位置3种情况;到lst的时候根据情况选择2种能保留x的运算;对于lst之后的op取能保留x的2种运算。以此可以列出组合数式子

对于找对于x的lst,知乎里提到了一个经典模型,即区间覆盖染色,每个位置最后的颜色,倒着并查集(不懂的可以去看看ACWing3115题解 : https://www.acwing.com/solution/content/60830/

pi 为第 i 个元素后面第一个 未被覆盖 的区间的 左端点

更新/并查集操作为 p[nw] = find(nw + 1), nw = find(nw);

借用下图:

在这里插入图片描述

code1:

#include "bits/stdc++.h"

using namespace std;

typedef long long ll;
const ll N = 3e5 + 10;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const ll mod = 998244353;

ll l[N], r[N], p[N];

ll find(ll x) {
    return (p[x] == x ? x : p[x] = find(p[x]));
}

ll qpow(ll x, ll n) {
    if (n <= 0) return 1;
    ll res = 1;
    while (n) {
        if (n & 1) res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

    ll n;
    cin >> n;
    for (ll i = 1; i <= n; ++i) cin >> l[i] >> r[i];

    for (ll i = 0; i <= 3e5 + 5; ++i) p[i] = i;

    ll ans = 0;
    for (ll i = n; i >= 1; --i) {
        ll nw = find(l[i]);
        while (nw <= r[i]) {
            if (i != 1) ans = (ans + qpow(3, i - 2) * qpow(2, n - i + 1) % mod) % mod;
            else ans = (ans + qpow(2, n - 1)) % mod;
            p[nw] = find(nw + 1);
            nw = find(nw);
        }
    }

    cout << ans << endl;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
"educational codeforces round 103 (rated for div. 2)"是一个Codeforces平台上的教育性比赛,专为2级选手设计评级。以下是有关该比赛的回答。 "educational codeforces round 103 (rated for div. 2)"是一场Codeforces平台上的教育性比赛。Codeforces是一个为程序员提供竞赛和评级的在线平台。这场比赛是专为2级选手设计的,这意味着它适合那些在算法和数据结构方面已经积累了一定经验的选手参与。 与其他Codeforces比赛一样,这场比赛将由多个问题组成,选手需要根据给定的问题描述和测试用例,编写程序来解决这些问题。比赛的时限通常有两到三个小时,选手需要在规定的时间内提交他们的解答。他们的程序将在Codeforces的在线评测系统上运行,并根据程序的正确性和效率进行评分。 该比赛被称为"educational",意味着比赛的目的是教育性的,而不是针对专业的竞争性。这种教育性比赛为选手提供了一个学习和提高他们编程技能的机会。即使选手没有在比赛中获得很高的排名,他们也可以从其他选手的解决方案中学习,并通过参与讨论获得更多的知识。 参加"educational codeforces round 103 (rated for div. 2)"对于2级选手来说是很有意义的。他们可以通过解决难度适中的问题来测试和巩固他们的算法和编程技巧。另外,这种比赛对于提高解决问题能力,锻炼思维和提高团队合作能力也是非常有帮助的。 总的来说,"educational codeforces round 103 (rated for div. 2)"是一场为2级选手设计的教育性比赛,旨在提高他们的编程技能和算法能力。参与这样的比赛可以为选手提供学习和进步的机会,同时也促进了编程社区的交流与合作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值