Codeforces 1461F - Mathematical Expression(分类讨论+找性质+dp)
阅读原文时间:2023年07月08日阅读:2

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

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章