• Educational Codeforces Round 137 (Rated for Div. 2) DE


    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
  • 相关阅读:
    Hadoop入门(二):手把手带你从零基础到完整安装配置
    基于安卓android微信小程序的校园维修平台
    go 链表
    C++,day0907
    vscode使用远程服务器jupyter
    Docker——Windows版本Docker安装
    Python 基于OpenCV+face_recognition+tkinter实现人脸特征监测
    跨境电商应该用什么样的服务器?多大带宽?
    【C语言】库宏offsetof
    wallys WiFi 6E Card,802.11ax,IPQ6010/QCN9074 QCN9024 802.11ax 4x4 MU-MIMO 5GHz
  • 原文地址:https://blog.csdn.net/qq_39602052/article/details/127425377