现场 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. [BUAA]起点 软工第一次作业-热身

    项目 内容 这个作业属于哪个课程 2020计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 第一次作业-热身! 我在这个课程的目标是 了解软件开发,提高自己的工程能力和团队协作能力 这个作业在哪 ...

  2. 大牛针对零基础入门c语言详解指针(超详细)

    C语言指针说难不难但是说容易又是最容易出错的地方,因此不管是你要做什么只要用到C指针你就跳不过,今天咱们就以 十九个例子来给大家简单的分析一下指针的应用,最后会有C语言视频资料提供给大家更加深入的参考 ...

  3. Vagrant 搭建开发环境实践

    介绍 Development Environments Made Easy -官网标题 vagrant是一个命令行的虚拟机管理程序.用于简化搭建开发环境. vagrant使用ruby语言基于Chef ...

  4. Verdi Protocol Analyzer Debug 简单使用

    转载:Verdi Protocol Analyzer Debug 简单使用_Holden_Liu的博客-CSDN博客_verdi 技巧 文档与源码: User Guide: UVMDebugUserG ...

  5. python re:正则表达式中使用变量

    参考:https://www.cnblogs.com/songbiao/p/12422632.html Python中正则表达式的写法,核心就是一个字符串.如下:re.compile(r'表达式')所 ...

  6. AGC036 A-Triangle | 构造

    题目链接 题意: 给出一个数$S(1\leqslant S \leqslant 10^{18})$. 要求在平面直角坐标系中找到三个点$(X_1,Y_1),(X_2,Y_2),(X_3,Y_3)$,满 ...

  7. MySQL到底能否解决幻读问题

    先说结论,MySQL 存储引擎 InnoDB 在可重复读(RR)隔离级别下是解决了幻读问题的. 方法:是通过next-key lock在当前读事务开启时,1.给涉及到的行加写锁(行锁)防止写操作:2. ...

  8. .Net Core微服务——网关(2):ocelot集成consul

    有consul基础的都知道,consul可以发现新增的服务,剔除掉无效的服务,赋予应用自动伸缩的能力.而ocelot如果集成了consul,那ocelot也能拥有这些能力,还可以自主选择负载均衡策略, ...

  9. 设计模式学习-使用go实现原型模式

    原型模式 定义 代码实现 优点 缺点 适用场景 参考 原型模式 定义 如果对象的创建成本比较大,而同一个类的不同对象之间差别不大(大部分字段都相同),在这种情况下,我们可以利用对已有对象(原型)进行复 ...

  10. Java 网络编程 - 总结概述

    IP地址 IP地址IntAddress 唯一定位一台网络上的计算机 127.0.0.1:本地localhost IP地址的分类 ipV4/ipV6 ipV4:127.0.0.1,4个字节组成:0~25 ...