刷题之Codeforces Round #769 (Div. 2)

刷题之Codeforces Round #769 (Div. 2)

1632A. ABC

  • 题目链接:ABC

  • 题目:判断 01 字符串中是否包含回文子串

  • 思路

    1. ∣ s ∣ ≥ 3 |s|\ge 3 s3 :一定不满足
    2. ∣ s ∣ = 2 |s|=2 s=2 s s s 00 00 00 11 11 11 时是回文
    #include<bits/stdc++.h>
    using namespace std;
    #define rep(i, a, b) for(int i = (a); i <= (b); i++)
    #define per(i, a, b) for(int i = (a); i >= (b); i--)
    #define ll long long
    #define db double
    #define VI vector<int>
    #define PII pair<int, int>
    const db Pi = 3.141592653589793;
    const int INF = 0x7fffffff;
    const int N = 1e5 + 5;
    const db eps = 1e-10;
    int cas, n;
    string s;
    int main(){
        cin >> cas;
        while(cas--){
            cin >> n >> s;
            if(n >= 3 || (n == 2 && s[0] == s[1])) puts("NO");
            else puts("YES");
        }
    }
    

1632B. Roof Construction

  • 题目链接:Roof Construction

  • 题目:给 0 ∼ n − 1 0\sim n-1 0n1 的一种排列,使得相邻两数的异或和的最大值 ( max ⁡ 1 ≤ i ≤ n − 1 p i ⊕ p i + 1 ) \left( \max\limits_{1\le i\le n - 1} p_i\oplus p_{i+1} \right) (1in1maxpipi+1) 最小

  • 类型:构造题

  • 思路:将 0 ∼ n − 1 0\sim n - 1 0n1 二进制展开后发现, n − 1 n-1 n1 内的二进制最高位为 1 1 1 与一个最高位为 0 0 0 的数字异或后,最高位一定为 1 1 1,且这个异或后的 1 1 1 是不可避免的。于是我们可以想办法使 max ⁡ 1 ≤ i ≤ n − 1 p i ⊕ p i + 1 = \max\limits_{1\le i\le n - 1} p_i\oplus p_{i+1}= 1in1maxpipi+1= 1 << (int)log2(n - 1) ,即最高位的 1 1 1

    • 只需要将所有最高位为 1 1 1 的相邻,所有最高位为 0 0 0 的相邻。只在需要保证交界处的异或和只在最高位为 1 1 1,且在其他后面的位置均为 0 0 0,于是想到用 0 0 0 调整。
    #include<bits/stdc++.h>
    using namespace std;
    #define rep(i, a, b) for(int i = (a); i <= (b); i++)
    #define per(i, a, b) for(int i = (a); i >= (b); i--)
    #define ll long long
    #define db double
    #define VI vector<int>
    #define PII pair<int, int>
    const db Pi = 3.141592653589793;
    const int INF = 0x7fffffff;
    const int N = 1e5 + 5;
    const db eps = 1e-10;
    int cas, n, a;
    int main(){
        cin >> cas;
        while(cas--){
            cin >> n;
            a = (1 << (int)log2(n - 1));
            rep(i, 1, a - 1) printf("%d ", i);
            printf("%d ", 0);
            rep(i, a, n - 1) printf("%d ", i);
            cout << endl;
        }
    }
    /*
    4
    2
    3
    5
    10
    */
    

1632C. Strange Test

  • 题目链接:Strange Test

  • 题目:给两个数 a , b    ( 1 ≤ a < b ≤ 1 0 6 ) a,b\;(1\le a<b\le 10^6) a,b(1a<b106) 和三个操作 a++b++a |= b,求最少操作次数使得 a = b a=b a=b

  • 类型:位运算,构造题

  • 思路

    • 结论一:或运算操作最多只能用一次。

      证明:由于当 a > b a>b a>b 后只能通过 b++ 使得 a = b a=b a=b。且 a |= b 后, a a a 不会减小且 a ≥ b a\ge b ab。这时只能进行 b++ 操作来满足条件。故为使操作次数最少,即使 a − b a-b ab 最小,不可再进行 a |= b 操作。

    • 考虑 a , b a,b a,b 只经过 a++b++ 操作后,变为 a ′ , b ′ a',b' a,b​ 再进行 a |= b 操作和 b++ 操作。一共需要的次数为
      ( a ′ − a ) + ( b ′ − b ) + 1 + ( a ′ ∣ b ′ − b ′ ) = ( a ′ + a ′ ∣ b ′ ) + ( 1 − a − b ) (a'-a)+(b'-b)+1 + (a'|b'-b')=(a'+a'|b')+(1-a-b) (aa)+(bb)+1+(abb)=(a+ab)+(1ab)
      只需使 a ′ + a ′ ∣ b ′ a'+a'|b' a+ab 最小。由于 a ′ ∈ [ a , b ] a'\in [a,b] a[a,b],因此可暴力枚举 a ′ a' a 来寻找满足的 b ′ b' b

    • 结论二:当 a |= b 操作前,可使 a , b a,b a,b 中一个不变,此时不影响结果。

      证明:真没想出来,若有大佬可证明,请务必评论留言。其实这个结论不用也可做出,代码见此博客

    #include<bits/stdc++.h>
    using namespace std;
    #define rep(i, a, b) for(int i = (a); i <= (b); i++)
    #define per(i, a, b) for(int i = (a); i >= (b); i--)
    #define ll long long
    #define db double
    #define VI vector<int>
    #define PII pair<int, int>
    const db Pi = 3.141592653589793;
    const int INF = 0x7fffffff;
    const int N = 1e5 + 5;
    const db eps = 1e-10;
    int cas, a, b, ans;
    int main(){
        cin >> cas;
        while(cas--){
            cin >> a >> b;
            ans = b - a;
            rep(i, a, b)  //只a++
                if((i | b) == b) ans = min(ans, i - a + 1);
            rep(i, b, 5 * b)  //只b++
                if((i | a) == i) ans = min(ans, i - b + 1);
            cout << ans << endl;
        }
    }
    /*
    5
    1 3
    5 8
    2 5
    3 19
    56678 164422
    */
    

1632D. New Year Concert

  • 题目链接:New Year Concert

  • 题目:对序列 { a } i = 1 n \{a\}_{i=1}^n {a}i=1n 的前缀 { a } i = 1 k \{a\}_{i=1}^k {a}i=1k 求最少修改 a i a_i ai 的数量 f ( { a } i = 1 k ) f\left(\{a\}_{i=1}^k \right) f({a}i=1k) ,使得 { a } i = 1 k \{a\}_{i=1}^k {a}i=1k 中任一连续子区间均满足 gcd ⁡ ( a l , a l + 1 , . . . , a r ) ≠ r − l + 1 \gcd(a_l,a_{l+1},...,a_r)\ne r-l+1 gcd(al,al+1,...,ar)=rl+1

  • 类型ST表,贪心

  • 思路

    • 首先发现若 gcd ⁡ ( a l , a l + 1 , . . . , a r ) = r − l + 1 \gcd(a_l,a_{l+1},...,a_r)= r-l+1 gcd(al,al+1,...,ar)=rl+1 只需将 { a } i = l r \{a\}_{i=l}^r {a}i=lr 内某一个数改为一个未出现的超大素数,即可使得 gcd ⁡ ( a l , a l + 1 , . . . , a r ) = 1 \gcd(a_l,a_{l+1},...,a_r)=1 gcd(al,al+1,...,ar)=1 满足条件
    • 假设固定 a l a_l al 改变 a r a_r ar 发现:当 r ↑ r\uparrow r 时, gcd ⁡ ( a l , a l + 1 , . . . , a r ) ↓ \gcd(a_l,a_{l+1},...,a_r)\downarrow gcd(al,al+1,...,ar) r − l + 1 ↑ r-l+1\uparrow rl+1. 故在固定 a l a_l al 时最多只存在一个 a r a_r ar,使得 gcd ⁡ ( a l , a l + 1 , . . . , a r ) = r − l + 1 \gcd(a_l,a_{l+1},...,a_r)= r-l+1 gcd(al,al+1,...,ar)=rl+1。因此整个序列 { a } i = 1 n \{a\}_{i=1}^n {a}i=1n 中最多有 n n n 个不满足条件的子区间
    • 步骤
      • 利用 ST表 预处理可 O ( 1 ) O(1) O(1) 求出任意连续子区间的 gcd ⁡ \gcd gcd
      • 1 ∼ n 1\sim n 1n 遍历左端点 a l a_l al二分右端点寻找使得 gcd ⁡ ( a l , a l + 1 , . . . , a r ) = r − l + 1 \gcd(a_l,a_{l+1},...,a_r)= r-l+1 gcd(al,al+1,...,ar)=rl+1 的右端点(若有)
      • 存下所有不满足条件的子区间,贪心寻找至少修改数字的数量:按照右端点排序,每次修改右端点 a n o w a_{now} anow (使右端点为一个超大素数),并删除所有包含 a n o w a_{now} anow 的子区间,最后统计答案即可。
    //st表, greedy
    #include<bits/stdc++.h>
    using namespace std;
    #define rep(i, a, b) for(int i = (a); i <= (b); i++)
    #define per(i, a, b) for(int i = (a); i >= (b); i--)
    #define ll long long
    #define db double
    #define VI vector<int>
    #define PII pair<int, int>
    const db Pi = 3.141592653589793;
    const int INF = 0x7fffffff;
    const int N = 2e5 + 5;
    const db eps = 1e-10;
    int n, modify[N], cnt, before, ans;
    int a[N], st[N][30];
    int gcd(int a, int b){
        return (b ? gcd(b, a % b) : a);
    }
    struct AC{
        int l, r;
    }b[N];
    bool cmp(AC a, AC b){
        if(a.r == b.r) return a.l < b.l;
        return a.r < b.r;
    }
    int check(int l, int r){
        int k = (int)log2(r - l + 1);
        return gcd(st[l][k], st[r - (1 << k) + 1][k]);
    }
    int find(int left){
        if(a[left] == 1) return left;
        int l = left, r = n + 1, mid;
        int ok = 0, GCD;
        while(l + 1 < r){
            mid = (l + r) / 2;
            GCD = check(left, mid);
            if(GCD == mid - left + 1){
                ok = 1;
                break;
            }
            if(GCD < mid - left + 1) r = mid;
            else l = mid;
        }
        if(!ok && r - l == 1)
            if(gcd(a[l], a[r]) == 2) ok = 1, mid = r;
        return ok ? mid : 0;
    }
    int main(){
        cin >> n;
        rep(i, 1, n) cin >> a[i];
        rep(j, 0, (int)log2(n) + 1){  //st表预处理区间gcd
            rep(i, 1, n + 1 - (1 << j)){
                if(j == 0) st[i][j] = a[i];
                else st[i][j] = gcd(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
            }
        }
        cnt = 0;
        rep(i, 1, n){  //固定左端点
            int tmp = find(i);
            if(tmp) b[++cnt] = AC{i, tmp};
        }
        //贪心修改
        sort(b + 1, b + 1 + cnt, cmp);
        before = 0;
        memset(modify, 0, sizeof(modify));
        rep(i, 1, cnt){
            if(b[i].l > before){
                modify[b[i].r] = 1;
                before = b[i].r;
            }
        }
        //统计前缀答案
        ans = 0;
        rep(i, 1, n){
            ans += modify[i];
            printf("%d ", ans);
        }
    }
    /*
    7
    2 12 4 8 18 3 6
    */
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值