现场 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
+*
*/

Codeforces 1461F - Mathematical Expression(分类讨论+找性质+dp)的更多相关文章

  1. Codeforces 1067E - Random Forest Rank(找性质+树形 dp)

    Codeforces 题面传送门 & 洛谷题面传送门 一道不知道能不能算上自己 AC 的 D1E(?) 挺有意思的结论题,结论倒是自己猜出来了,可根本不会证( 开始搬运题解 ing: 碰到这样 ...

  2. Codeforces 1383C - String Transformation 2(找性质+状压 dp)

    Codeforces 题面传送门 & 洛谷题面传送门 神奇的强迫症效应,一场只要 AC 了 A.B.D.E.F,就一定会把 C 补掉( 感觉这个 C 难度比 D 难度高啊-- 首先考虑对问题进 ...

  3. Codeforces 809C - Find a car(找性质)

    Codeforces 题目传送门 & 洛谷题目传送门 首先拿到这类题第一步肯定要分析题目给出的矩阵有什么性质.稍微打个表即可发现题目要求的矩形是一个分形.形式化地说,该矩形可以通过以下方式生成 ...

  4. CodeForces 788B - Weird journey [ 分类讨论 ] [ 欧拉通路 ]

    题意: 给出无向图. good way : 仅有两条边只经过一次,余下边全经过两次的路 问你共有多少条不同的good way. 两条good way不同仅当它们所经过的边的集合中至少有一条不同 (很关 ...

  5. 洛谷 P7156 - [USACO20DEC] Cowmistry P(分类讨论+trie 树上 dp)

    题面传送门 题意: 给出集合 \(S=[l_1,r_1]\cup[l_2,r_2]\cup[l_3,r_3]\cup\dots\cup[l_n,r_n]\) 和整数 \(k\),求有多少个三元组 \( ...

  6. Codeforces 521E - Cycling City(点双连通分量+分类讨论)

    Codeforces 题面传送门 & 洛谷题面传送门 大家都是暴力找生成树然后跳路径,代码不到 50 行(暴论)的一说--好,那本蒟蒻决定提供一种代码 150 行,但复杂度也是线性的分类讨论做 ...

  7. Codeforces 1513F - Swapping Problem(分类讨论+乱搞)

    Codeforces 题目传送门 & 洛谷题目传送门 简单题,难度 *2500 的 D2F,就当调节一下一模炸裂了的自闭的心情,稍微写写吧. 首先我看到这题的第一反应是分类讨论+数据结构,即枚 ...

  8. HDU5957 Query on a graph(拓扑找环,BFS序,线段树更新,分类讨论)

    传送门:http://acm.hdu.edu.cn/showproblem.php?pid=5957 题意:D(u,v)是节点u和节点v之间的距离,S(u,v)是一系列满足D(u,x)<=k的点 ...

  9. CodeForces - 789B B. Masha and geometric depression---(水坑 分类讨论)

    CodeForces - 789B 当时题意理解的有点偏差,一直wa在了14组.是q等于0的时候,b1的绝对值大于l的时候,当b1的绝对值大于l的时候就应该直接终端掉,不应该管后面的0的. 题意告诉你 ...

随机推荐

  1. Clusternet v0.5.0 重磅发布: 全面解决多集群应用分发的差异化配置难题

    作者 徐迪,腾讯云容器技术专家. 汝英哲,腾讯云高级产品经理. 摘要 在做多集群应用分发的时候,经常会遇到以下的差异化问题,比如: 在分发的资源上全部打上统一的标签,比如 apps.my.compan ...

  2. NOIP模拟83(多校16)

    前言 CSP之后第一次模拟赛,感觉考的一般. 不得不吐槽多校联测 OJ 上的评测机是真的慢... T1 树上的数 解题思路 感觉自己思维有些固化了,一看题目就感觉是线段树. 考完之后才想起来这玩意直接 ...

  3. [no code][scrum meeting] Beta 11

    $( "#cnblogs_post_body" ).catalog() 例会时间:5月26日11:30,主持者:肖思炀 下次例会时间:5月27日11:30,主持者:乔玺华 一.工作 ...

  4. Sharding-JDBC自定义复合分片算法

    Sharding-JDBC自定义复合分片算法 一.背景 二.需求 1.对于客户端操作而言 2.对于运营端操作而言 三.分片算法 1.客户id和订单id的生成规则 2. 确定数据落在那个表中 3.举例说 ...

  5. 嵌入式物联网之SPI接口原理与配置

    本实验采用W25Q64芯片 W25Q64是华邦公司推出的大容量SPI FLASH产品,其容量为64Mb.该25Q系列的器件在灵活性和性能方面远远超过普通的串行闪存器件.W25Q64将8M字节的容量分为 ...

  6. 转:VCS仿真vivado IP的方法

    vivado中的仿真库和模型与ISE中的是不一样的,因此在vivado中使用VCS进行仿真的方法也与ISE中不一样. VCS可以通过两种方法对XILINX的器件进行功能仿真和门级仿真,这两种方法是 P ...

  7. 视频编码GOP

    GOP group of pictures GOP 指的就是两个I帧之间的间隔. 比较说GOP为120,如果是720 p60 的话,那就是2s一次I帧. 在视频编码序列中,主要有三种编码帧:I帧.P帧 ...

  8. 空格替换 牛客网 程序员面试金典 C++ Python

    空格替换 牛客网 程序员面试金典 C++ Python 题目描述 请编写一个方法,将字符串中的空格全部替换为"%20".假定该字符串有足够的空间存放新增的字符,并且知道字符串的真实 ...

  9. AtCoder Beginner Contest 220部分题(G,H)题解

    刚开始的时候被E题卡住了,不过发现是个数学题后就开始使劲推式子,幸运的是推出来了,之后的F题更是树形DP换根的模板吧,就草草的过了,看了一眼G,随便口胡了一下,赶紧打代码,毕竟时间不多了,最后也没打完 ...

  10. Spring Cloud 微服务实战——nacos 服务注册中心搭建(附源码)

    作为微服务的基础功能之一的注册中心担任重要的角色.微服务将单体的服务拆分成不同的模块下的服务,而不同的模块的服务如果进行通信调用呢?这就需要服务注册与发现.本文将使用阿里开源项目 nacos 搭建服务 ...