Educational Codeforces Round 184 (Rated for Div. 2)

A. Alice and Bob

https://codeforces.com/contest/2169/problem/A

对于这一道题,关键观察:固定最优解位置仅存在于Alice两侧差1的位置。

原因:我们把球抽象成一条轴,两人将轴分成1或2或3段,我们先简单来想:(排序后)。

复制代码
    1.先考虑2段的情况,0000AB00,很容易发现,A左边的一定A加分,B右边的一定B加分,而A的位置是确定了的,为了保证B最大,A的两侧是一个很好的判断位置。
    2.然后我们考虑3段的情况,0000A00B00,发现这时A右边和B左边和1处理一样,而中间的部分多了不确定性,存在一个点,在该点左边给A,右边给B。
    3.最后考虑一种特例,00000AB,这种情况只需要同1相同思想处理即可
    结合1,2我们可以发现:对于B来说2一定不优于1,只有2的中间元素均给B加分时,1和2才同样最优,因此,我们舍弃2,只考虑1和3.

代码实现:

1.排序

2.如果A<=line.front(),B=A+1; A>=line.back(),B=A-1;

3.二分找到A的位置,upper_bound()和lower_bound(),然后计算两段长度,取大的一边,对B进行加减

复制代码
void solve(){
    int n, a;
    cin >> n >> a;
    vector<int> line(n);
    for(auto &it : line){
        cin >> it;
    }
    sort(line.begin(), line.end());
    if(line.front() >= a){
        cout << a + 1 << "\n";
        return;
    }
    else if(line.back() <= a){
        cout << a - 1 << "\n";
        return;
    }
    else{
        aauto it=upper_bound(line.begin(),line.end(),a);
        int size1=line.end()-it;
        it=lower_bound(line.begin(),line.end(),a);
        int size2=it-line.begin();
        if(size1<size2){
            cout<<a-1<<"\n";
        }else{
            cout<<a+1<<"\n";
        }
    }
}

B. Drifting Away

https://codeforces.com/contest/2169/problem/B

对于这一道题,其实挺简单的,只是模拟就可以了,可能细节比较多吧,我wa了好几次,最后发现应该是-1外的情况错了,又全部改了一遍,思路:

1.将-1的情况判断出来。

2.处理不是-1的情况(因为1已经处理了很多可能的形式,致使最后的形式很简单,只需要记住*+max(<,>),就行了,当然,连续的)

复制代码
void solve() {
    string line;
    cin >> line;
    
    // 特殊情况处理:字符串长度为1
    if (line.size() == 1) {
        cout << 1 << "\n";
        return;
    }
    
    // 检查字符串中是否存在非法字符组合
    for (int i = 0; i < line.size() - 1; i++) {
        string t = line.substr(i, 2);
        // 检测四种非法组合:"**", "><", ">*", "*<"
        if (t == "**" || t == "><" || t == ">*" || t == "*<") {
            cout << -1 << "\n";
            return;
        }
    }
    
    int pre1 = 0, pre2 = 0;
    
    // 从左向右扫描,统计连续的'<'或'*'的数量
    for (int i = 0; i < line.size(); i++) {
        if (line[i] == '<' || line[i] == '*') {
            pre1++;
        } else {
            break;
        }
    }
    
    // 从右向左扫描,统计连续的'>'或'*'的数量
    for (int i = line.size() - 1; i >= 0; i--) {
        if (line[i] == '>' || line[i] == '*') {
            pre2++;
        } else {
            break;
        }
    }
    
    // 输出两个方向扫描结果的最大值
    cout << max(pre1, pre2) << "\n";
    return;
}

C. Range Operation

https://codeforces.com/contest/2169/problem/C

看到的第一眼我就觉的是dp但是无从下手,只想到了可能要从后向前处理以下,打完之后群u发的数学公式让我豁然开朗,原来用公式计算一遍就会这样美妙,就可以枚举了。

这样,我们就可以预处理前半部分,在枚举后半部分就好了OvO,但是还是又改了好久最后还是群u提醒才改对的。

代码实现:

1.处理前缀和,方便运算。

2.建立后缀数组维护最大值,降低查询复杂度为O(1)。

3.枚举左端点,维护增量最大值。

4.输出答案(判断增量是否为负)
细节注意 一定要将 ixi和jxj转换为(long long)

复制代码
void solve(){
    int n;
    cin>>n;
    vector<int> line(n+1);
    vector<long long> pre1(1,0);
    long long sum=0;
    for(int i=1;i<=n;i++){
        cin>>line[i];
        sum+=line[i];
        pre1.push_back(sum);
    }
    long long ans=0;
    vector<long long> suf(n+2,LLONG_MIN);
    //后缀数组维护表达式左侧部分最大值
    for(int i=n;i>=1;i--){
        suf[i]=max(1LL*i*i+i-pre1[i],suf[i+1]);
    }
    //枚举j
    for(int j=1;j<=n;j++){
        long long r_val=1LL*j*j-j-pre1[j-1];
        ans=max(ans,suf[j]-r_val);    
    }
    cout<<max(sum,sum+ans)<<"\n";
}