现场 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. Java Filter型内存马的学习与实践

    完全参考:https://www.cnblogs.com/nice0e3/p/14622879.html 这篇笔记,来源逗神的指点,让我去了解了内存马,这篇笔记记录的是filter类型的内存马 内存马 ...

  2. airtest keyevent 按键速查表

  3. Canal Server发送binlog消息到Kafka消息队列中

    Canal Server发送binlog消息到Kafka消息队列中 一.背景 二.需要修改的地方 1.canal.properties 配置文件修改 1.修改canal.serverMode的值 2. ...

  4. Mac上安装Grafana

    Mac上安装Grafana 一.背景 二.安装步骤 1.通过 Home Brew 安装 2.通过二进制包进行安装 1.下载 2.grafana配置文件的路径 3.修改grafana配置 1.修改默认的 ...

  5. springboot多配置环境

    在我们的开发过程中,经常会有多套配置环境,比如开发环境(dev),测试环境(test),生产环境(prod)等,在各个环境中我们需要使用到不同的配置,那么在springboot中是如何做到的呢? 1. ...

  6. dice_game攻防世界进阶区

    dice_game XCTF 4th-QCTF-2018 前言,不得不说,虽然是个简单题但是还是要记录一下,来让自己记住这些东西. 考察的知识点是: 1.cdll_loadlibrary加载对应库使得 ...

  7. 『学了就忘』Linux基础 — 4、VMware安装

    目录 1.VMware介绍 2.VMware主要特点 3.VMware建议配置 4.VMware安装 1.VMware介绍 VMware是一个虚拟PC的软件,可以在现有的操作系统上虚拟出一个新的硬件环 ...

  8. Spring Cloud Gateway夺命连环10问?

    大家好,我是不才陈某~ 最近有很多小伙伴私信我催更 <Spring Cloud 进阶>,陈某也总结了一下,最终原因就是陈某之前力求一篇文章将一个组件重要知识点讲透,这样导致了文章篇幅很长, ...

  9. linux wifi热点服务脚本

    最近有关wifi热点的驱动,启动参数都调试完了,验证可以连接传输数据. 首先要在系统启动脚本中插入wifi驱动,配置wlan0的ip insmod /system/vendor/modules/818 ...

  10. Luogu P1563 [NOIp2016提高组]玩具谜题 | 模拟

    题目链接 纯模拟题,没啥好说的,就是要判断地方有点多,一定要注意细节. #include<iostream> #include<cstdio> #include<fstr ...