当前位置:   article > 正文

cf #832 Div.2(A-D)_cf832div2

cf832div2

Cf #832 Div.2

A. Two Groups

  • 题意

    • 给一个数组,将其分为两组s1,s2,问|sum(s1)|-|sum(s2)|的最大值
  • 题解

    • 贪心。要求差值最大,那么|sum(s1)|尽量大,|sum(s2)|尽量小。而s1中放同一种符号的数会最大,此时来验证s1中放一种符号,s2中放另一种符号的数是最优
    • 假设s1中放的是正数会使得|sum(s1)|最大,那么负数全部放在s2中,假设负数的和为-x,这个时候会发现,不管分不分组所求式子的值是不变的,|sum(s1)|-|-x|=|sum(s1)-x|,设s1中放的是负数会使得|sum(s1)|最大同理,所以答案是固定的为|sum|
  • 代码

#include <iostream>

using namespace std;

void solve() {
    int n; cin>>n;
    long long x,sum=0;//注意数据范围
    for(int i=0;i<n;i++) cin>>x,sum+=x;
    cout<<abs(sum)<<'\n';
}

int main() {
    int t=1; cin>>t;
    while(t--) solve();
    
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

B. BAN BAN

  • 题意

    • 给定含有n个连续"BAN"的字符串s,每次可以选择两个位置交换字符,问最少操作多少次可以使得s中不存在"BAN"的子序列
  • 题解

    • 贪心。让N尽量在前面,B尽量在后面,贪心一下即让最后面的N与最前面的B交换,可得操作数为(n+1)/2
    • 证明:因为每个BAN本身会让s中有BAN,所以每一个连续的BAN都一定要被操作到,而每一次贪心操作都会让两端的BAN对s没有BAN贡献,所以操作次数为(n+1)/2,操作的下标如上一条所示
  • 代码

#include <iostream>

using namespace std;

void solve() {
    int n; cin>>n;
    cout<<(n+1)/2<<'\n';
    for(int i=0;i<(n/2);i++) 
        cout<<i*3+1<<' '<<3*n-3*i<<'\n';
    if(n&1) cout<<(n+1)/2*3-2<<' '<<(n+1)/2*3<<'\n';
}

int main() {
    int t=1; cin>>t;
    while(t--) solve();
    
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

C. Swap Game

  • 题意

    • 给一个数组a,Alice和Bob轮流操作
    • 操作:如果此时a1=0,那么输;否则将a1–,同时选另一个位置j,swap(a1,aj)
  • 题解

    • 博弈论。结论为a1为最小值那么先手必败,否则先手必胜
    • 如果a1是最小的,先手先把a1–,然后换走,但是后手操作时又把最小的换回到a1,因为a1是最容易减到0的,后手为了快点结束游戏,那么肯定每次都把最小值放在a1位置,所以每一次都是先手要把a1–,所以a1一定被先手减为0
    • 如果a1不是最小的,那么先手Alice会把最小的放到a1,此时回到上一次讨论情况Bob必败
  • 代码

#include <iostream>

using namespace std;

void solve() {
    int n,x,mi; cin>>n;
    bool f=0;
    for(int i=1;i<=n;i++) {
        cin>>x;
        if(i==1) mi=x;
        if(x<mi) f=1;
    }
    if(!f) cout<<"Bob"<<'\n';
    else cout<<"Alice"<<'\n';
}

int main() {
    int t=1; cin>>t;
    while(t--) solve();
    
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

D. Yet Another Problem

  • 题意

    • 给定一个长度为n的数组a,以及q个询问,每次询问一个区间[L,R],问区间经过下列操作能否将[L,R]元素全部变为0
    • 操作:在[L,R]中选出一个长度为奇数的子区间[l,r],将[l,r]全部变为区间的异或和的值
  • 题解

    • 思维+前缀和预处理思想+STL。前缀和看是否全为0,前缀异或和,当x[r]=x[l]时,有XOR[l,r]=0
    • 首先操作不会改变区间异或和的值,因为[l,r]改变前其异或和为x,改变之后有奇数个x,其异或和依旧为x;所以当XOR[L,R]!=0,那么无解
    • 否则对于XOR[L,R]==0的情况
      • 如果[L,R]都是0,那么无需操作已经达到目标,所以操作数为0
      • 如果区间长度为2,那么无法通过操作使其符合要求,无解
      • 如果[L,R]区间长度为奇数,那么直接操作[L,R]一次即可,操作数为1
      • 如果[L,R]区间长度为偶数,但区间两端有0导致区间长度可以看为为奇数,那么同上一条,只操作一次有效区间,操作数为1
      • 如果[L,R]有效区间长度为偶数,那么需要把区间分为两段长度为奇数的段,设分界点为x,且XOR[L,x]=XOR[L,R]=0
    • 如何更高效的找到x捏?方法1就是预处理出以i结尾的区间前面符合要求的最近的x;另一种方法就是先分奇偶性存一下异或和相同的下标,然后在线二分找,代码直接lowerbound了
  • 代码

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

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);

    ll n,q; cin >> n >> q;
    vector<ll> a(n);
    for(ll i=0;i<n;++i) cin >> a[i];
    vector<ll> b(n),c(n);//前缀和,异或前缀和
    for(ll i=0;i<n;++i){
        b[i] = a[i];
        c[i] = a[i];
        if(i > 0){
            b[i] ^= b[i-1];
            c[i] += c[i-1];
        }
    }
    vector<map<ll,vector<ll> > > h(2);
    for(ll i=0;i<n;++i) h[i%2][b[i]].push_back(i);//预处理异或和相同且奇偶性相同的下标
    
    while(q--){
        ll l,r;
        cin >> l >> r; --l; --r;//下标调整
        
        if((b[r]^(l == 0 ? 0 : b[l-1])) != 0){//异或和不为0
            cout << -1 << "\n";
            continue;
        }
        if(c[r]-(l == 0 ? 0 : c[l-1]) == 0){//异或和为0且全是0
            cout << 0 << "\n";
            continue;
        }
        if((r-l+1)%2 == 1 || a[l] == 0 || a[r] == 0){//异或和为0,且有效区间可看成奇数长度
            cout << 1 << "\n";
            continue;
        }
        ll v = (l == 0 ? 0 : b[l-1]);
        auto it = lower_bound(h[l%2][v].begin(),h[l%2][v].end(),l);
        ll k = (it == h[l%2][v].end() ? n : *it);//l后面离l最近的下标
        cout << (k < r ? 2 : -1) << "\n";//下标存在2次,不存在-1
    }
    return 0;
}
  • 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
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/寸_铁/article/detail/912625
推荐阅读
相关标签
  

闽ICP备14008679号

        
cppcmd=keepalive&