洛谷P7725 珍珠帝王蟹 分类讨论
洛谷P7725 珍珠帝王蟹 分类讨论
题意
给定\(n\)个op,具有符号\(*\)或者符号\(+\) 权值\(v\)
初始值为0,挑选一种操作顺序使得最终值最大
\[1 \leq n\leq 10^5\\
2 \leq |v| \leq 10^6
\]
分析
大概思路可以明确就是贪心
容易发现比较特殊且棘手的操作的就是乘上负数和加上负数
- 如果没用乘上负数的操作,那么加上负数的操作我们肯定希望影响越小越好,这个情况比较简单,只要先加上所有正数然后乘上所有正数然后减去所有负数即可
- 如果有乘上负数的操作 就需要讨论奇偶,因为我们一定希望结果是正数。
-
- 如果乘负数的个数是奇数个,那么要让最后的数是正数,只需要先把所有负数加上,然后用绝对值最小的负数去乘上 当前数。这样当前数就成为了非负数,且有偶数个乘负数操作,接下来贪心即可,把所有正数加上,然后乘上剩下的数
- 如果乘负数的个数是偶数个,那么应该让所有正数先加上,然后把它乘上负数,然后用绝对值最小的负数去乘上当前数,这样当前数成为了负数,这么做是为了同时最大化加减数的贡献,这时再加上所有的负数,最后再乘上剩下的数即可
这样此题就做完了,大概思路就是让加减数先做,且保持正的性质
代码
#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
typedef long long ll;
ll rd(){
ll x;
scanf("%lld",&x);
return x;
}
const int MOD = 998244353;
int mul(int a,int b){
int res = (ll)a * b % MOD;
if(res < 0) res += MOD;
return res;
}
void add(int &a,int b){
a += b;
if(a >= MOD) a -= MOD;
}
void sub(int &a,int b){
a -= b;
if(a < 0) a += MOD;
}
char op[5];
int main(){
int n = rd();
vector<int> v1,v2,v3,v4; //+:+,- *:+,-
for(int i = 0;i < n;i++){
scanf("%s",op);
int val = rd();
if(op[0] == '+') {
if(val > 0) v1.push_back(val);
else v2.push_back(-val);
}
else {
if(val > 0) v3.push_back(val);
else v4.push_back(val);
}
}
int ans = 0;
sort(v1.begin(),v1.end());
sort(v2.begin(),v2.end());
sort(v3.begin(),v3.end());
sort(v4.begin(),v4.end());
if(v4.empty()) {
for(auto it:v1){
add(ans,it);
}
for(auto it:v3){
ans = mul(ans,it);
}
for(auto it:v2){
sub(ans,it);
}
}
else {
if(v4.size() & 1) {
for(auto it:v2){
sub(ans,it);
}
ans = mul(ans,v4.back());
v4.pop_back();
for(auto it:v1){
add(ans,it);
}
for(auto it:v3){
ans = mul(ans,it);
}
for(auto it:v4){
ans = mul(ans,it);
}
}
else {
for(auto it:v1){
add(ans,it);
}
ans = mul(ans,v4.back());
v4.pop_back();
for(auto it:v2){
sub(ans,it);
}
for(auto it:v3){
ans = mul(ans,it);
}
for(auto it:v4){
ans = mul(ans,it);
}
}
}
cout << ans;
}