现场 1 小时 44 分钟过掉此题,祭之

大力分类讨论。

如果 \(|s|=1\),那么显然所有位置都只能填上这个字符,因为你只能这么填。

scanf("%d",&n);mmp['+']=0;mmp['-']=1;mmp['*']=2;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
char opt[4];scanf("%s",opt+1);int len=strlen(opt+1);
int msk=0;for(int i=1;i<=len;i++) msk|=1<<mmp[opt[i]];
if(msk==1||msk==2||msk==4){
for(int i=1;i<=n;i++){
printf("%d",a[i]);if(i!=n) printf("%s",opt+1);
}
}

如果 \(s\) 只包含加和减,那么是个人都会全填加号。

else if(msk==3){
for(int i=1;i<=n;i++){
printf("%d",a[i]);if(i!=n) putchar('+');
}
}

如果 \(s\) 中只包含乘和减,假设 \(a_i\) 中第一个非零的位置为那么肯定只有最开头一段非零的数的贡献非零。剩下的贡献都为要么为零,要么为负。此时我们选择在最开头一段非零的数之间填 *,接下来填一个减号,后面全填乘号。由于后面的数中肯定存在一个 \(0\),所以减号后面的那坨东西肯定为 \(0\),而我们的填法又保证了减号前面的东西尽可能大。所以这样可以取到最大值。

else if(msk==6){
bool hav=0;printf("%d",a[1]);
for(int i=2;i<=n;i++){
if(!a[i]&&!hav) hav=1,putchar('-');
else putchar('*');
printf("%d",a[i]);
}
}

重头戏是 \(s\) 中既包含 + 又包含 * 的情况。

注意此时 \(s\) 中有没有 - 对我们的结果不会有影响。因为减号完全可以被加号代替并且结果不会更差。

显然 \(0\) 旁边只能填加号。假设 \(0\) 把原序列分成 \(l\) 段,我们可以对这 \(l\) 段分别计算贡献,再将它们相加,各自的贡献独立。

现在我们的目标就是在一段非零段之间填上 +* 使结果最大。

注意到如果这一段中不包含 \(1\) 那肯定全填乘号最优。唯一会影响我们判断结果的就是 \(1\)。

我们可以再用 \(1\) 把这个非 \(0\) 大段分成若干个“非 \(1\) 小段”,显然这些非 \(1\) 段之间全填 *。而相邻的 \(1\) 之间要么全填 + 要么全填 *

分析到这里,我们不妨把我们的发现用字母表示出来。假设我们在处理 \([L,R]\) 这个“非零大段”,共有 \([l_1,r_1],[l_2,r_2],\dots,[l_m,r_m]\) 这 \(m\) 个 “非 \(1\) 小段”。由于这 \(m\) 个“非 \(1\) 小段”之间都填 *,可以假设这 \(m\) 个“非 \(1\) 小段”中间所有数的乘积为 \(b_1,b_2,\dots,b_m\)。

首先可以肯定的一点是,\([L,l_1),(r_m,R]\) 之间所有数都为 \(1\),它们之间肯定只能填加号。

关键在于中间部分怎么填。不难看出这是一个基于段的划分,相邻段之间要么 + 要么 *。于是就有一个 \(m^2\) 的 \(dp\),\(dp_i\) 表示前 \(i\) 段,第 \(i\) 段与 \(i+1\) 段之间全 + 的最大值,枚举上 + 的位置转移即可。

然后我就想着用玄学乱搞优化这个 \(dp\),大概没啥思路,还要写高精。众所周知,CF 几乎不涉及高精,如果碰到道高精的题那大概率是你想偏了。

于是开始挖掘性质。我们可以发现,如果 \(b_i\) 的乘积大于 \(10^9\),那中间全 * 肯定是最优的。因为,假设最你在第 \(i\) 段与第 \(i+1\) 段之间填了 +,那么这样就算中间全是 \(1\),那最大也不过 \(5\times 10^8+2+10^5=500100002\),小于全填乘号的 \(10^9\)。

排除掉这种情况之后,\(m\) 最大也不过 \(\log_2(10^9)\),直接 \(m^2\) \(dp\) 是完全没问题的,这样可以通过此题。

考场上的代码:

#include <bits/stdc++.h>
using namespace std;
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define mp make_pair
typedef long long ll;
typedef unsigned long long ull;
template<typename T>
void read(T &x){
char c=getchar();T neg=1;
while(!isdigit(c)){
if(c=='-') neg=-1;
c=getchar();
}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
x*=neg;
}
const int MAXN=1e5;
int n,a[MAXN+5];
map<char,int> mmp;
bool sgn[MAXN+5];
int pl[MAXN+5],pr[MAXN+5],pc=0;
ll pm[MAXN+5],dp[MAXN+5];int pre[MAXN+5];
void solve(int l,int r){
if(l>r) return;
pc=0;int lst=l-1;ll mul=1;
for(int i=l;i<=r;i++){
if(a[i]==1){
if(lst+1<=i-1) pl[++pc]=lst+1,pr[pc]=i-1,pm[pc]=mul;
mul=1;lst=i;
} else {
mul*=a[i];if(mul>1e9) mul=1e9;
}
}
if(lst!=r) pl[++pc]=lst+1,pr[pc]=r,pm[pc]=mul;
// for(int i=1;i<=pc;i++) printf("%d %d %d\n",pl[i],pr[i],pm[i]);
pr[0]=l-1;for(int i=1;i<=pc;i++) dp[i]=0;
mul=1;
for(int i=1;i<=pc;i++){
mul*=pm[i];if(mul>1e9) mul=1e9;
}
if(mul==1e9){
pre[pc]=1;
}
else{
for(int i=1;i<=pc;i++){
mul=1;
for(int j=i;j>=1;j--){
mul*=pm[j];
if(dp[i]<dp[j-1]+pl[j]-pr[j-1]-1+mul){
dp[i]=dp[j-1]+pl[j]-pr[j-1]-1+mul;
pre[i]=j;
}
}
// printf("%d %lld %d\n",i,dp[i],pre[i]);
}
}
for(int i=l;i<pl[1];i++) sgn[i]=1;
for(int i=pr[pc];i<r;i++) sgn[i]=1;
int cur=pc;
while(cur){
int t=pre[cur];//printf("%d\n",t);
for(int i=pr[t-1];i<pl[t];i++) sgn[i]=1;cur=t-1;
}
}
int main(){
// freopen("in.txt","r",stdin);
scanf("%d",&n);mmp['+']=0;mmp['-']=1;mmp['*']=2;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
char opt[4];scanf("%s",opt+1);int len=strlen(opt+1);
int msk=0;for(int i=1;i<=len;i++) msk|=1<<mmp[opt[i]];
if(msk==1||msk==2||msk==4){
for(int i=1;i<=n;i++){
printf("%d",a[i]);if(i!=n) printf("%s",opt+1);
}
} else if(msk==3){
for(int i=1;i<=n;i++){
printf("%d",a[i]);if(i!=n) putchar('+');
}
} else if(msk==6){
bool hav=0;printf("%d",a[1]);
for(int i=2;i<=n;i++){
if(!a[i]&&!hav) hav=1,putchar('-');
else putchar('*');
printf("%d",a[i]);
}
} else {
for(int i=1;i<=n;i++){
if(!a[i]){
if(i!=1) sgn[i-1]=1;
if(i!=n) sgn[i]=1;
}
}
int pre=0;
for(int i=1;i<=n;i++){
if(!a[i]) solve(pre+1,i-1),pre=i;
}
solve(pre+1,n);
for(int i=1;i<n;i++){
printf("%d",a[i]);
if(sgn[i]) putchar('+');
else putchar('*');
} printf("%d",a[n]);
}
return 0;
}
/*
12
1 1 2 3 1 3 1 1 1 0 1 1
+* 18
1 2 3 1 1 2 1 1 1 1 1 1 1 1 1 1 1 2
+*
*/

最新文章

  1. wdcp 下apache模式开启https访问,支持多站点
  2. Nginx泛解析的匹配域名绑定到子目录配置
  3. postgres配置主从流复制
  4. ROS Hotspot服务器的搭建与设定!(上网认证)
  5. 硬盘安装centos
  6. spring读取prperties配置文件(1)
  7. Windows phone 之Image控件
  8. react-native 入门资源合集
  9. postfix中recipient/client/sender/helo四者的区别&lt;转载&gt;
  10. Xamarin自定义布局系列——ListView的一个自定义实现ItemsControl(横向列表)
  11. tab切换插件开发
  12. SSH简单项目
  13. 839A Arya and Bran
  14. P1569 [USACO11FEB]属牛的抗议
  15. Task 6.2站立会议三
  16. Davinci-DM6467板子-外围器件的I2C地址的疑惑解答
  17. Linux中的阻塞机制
  18. abp部署端口和域名映射配置
  19. C++设计模式 -- 解析和实现
  20. C#和Java接口对比

热门文章

  1. 华为在HDC2021发布全新HMS Core 6 宣布跨OS能力开放
  2. 编程题:X星人的金币
  3. 【数据结构与算法Python版学习笔记】递归(Recursion)——定义及应用:分形树、谢尔宾斯基三角、汉诺塔、迷宫
  4. 基于Apache Zookeeper手写实现动态配置中心(纯代码实践)
  5. [对对子队]会议记录4.12(Scrum Meeting 3)
  6. [no code][scrum meeting] Alpha 6
  7. 2021.7.29考试总结[NOIP模拟27]
  8. 2021.6.29考试总结[NOIP模拟10]
  9. 关于QGIS的插件开发(C++)
  10. Vue:Vue的介绍以及组件剖析