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";
}