Codeforces 1461F dp+构造

题意

给你一个长度为 N N N a a a 数组,数组里面都是 [ 0 , 9 ] [0,9] [0,9] 的自然数,允许你添加 + + + − - ∗ * 号,现在你希望式子最大

N ≤ 1 0 5 N \leq 10^5 N105

分析

分析一下,只有 + + + ∗ * 的情况比较麻烦
考虑麻烦的点, 0 0 0的左右两边肯定都是 + + +
但是 1 1 1的左右两边,可以是 + + +,也可以是 ∗ * + + +是两边的数都不太大的情况,*是两边的数都大,就需要 ∗ *
而且可以发现, 1 1 1的左右两边,肯定是同号的。
所以把 0 0 0作为断点,找到区间 [ l , r ] [l,r] [l,r],然后发现前缀和后缀的1肯定都是需要 + + +的,因为没有 ∗ * 的必要。
中间数数有多少个 > 1 >1 >1的数,如果 > 28 >28 >28个左右的话,那么中间全都用乘号连接。
否则的话,可以考虑 d p dp dp来填。
d p [ i ] dp[i] dp[i]表示当前等式到达第 i i i位的最大值,记录一个路径 p a t h path path
容易写出:
d p [ i ] = m a x ( d p [ i − 1 ] + a [ i ] , d p [ j − 1 ] + Π k = j i a [ k ] ) ) dp[i] = max(dp[i-1] + a[i] , dp[j-1] + \Pi_{k=j}^{i} a[k])) dp[i]=max(dp[i1]+a[i],dp[j1]+Πk=jia[k]))
发现 a [ i ] = 1 a[i] = 1 a[i]=1其实可以直接从上一个转移过来的,实际枚举j的个数至多有 28 28 28个。
d p dp dp的时候要用__int128

代码

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dep(i,a,b) for(int i=(a);i>=(b);--i)
#define MP make_pair
#define fi first
#define se second
#define PB push_back
#define CL clear
#define pii pair<int,int>
#define int long long
using namespace std;
// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
const int N = (int)(5e5) + 10;
const int mod = 998244353;
const int inf = 1e18 + 10;
const long double pi = acos(-1);
inline int rd() {
  int p=0; int f=1; char ch=getchar();
  while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
  return p*f;
}
 
int a[N],len,n; char ans[N],s[N];

void wr() {
  for(int i=1;i<n;i++) printf("%lld%c",a[i],ans[i]);
  printf("%lld\n",a[n]);
}

bool check(char ch) {
  for(int i=1;i<=len;i++) if(s[i] == ch) return 1;
  return 0;
}

__int128 dp[N],path[N],mul[N];

void solve(int l,int r) {
  if(l>r) return ;
  dp[l-1] = 0; vector<int> v;
  mul[l-1] = 1; for(int i=l;i<=r;i++) mul[i] = a[i] * mul[i-1];
  for(int i=l;i<=r;i++) {
    // printf("%lld ",dp[i]); puts("");
    if(a[i] == 1) dp[i] = dp[i-1] + 1,path[i] = i;
    else {
      if(v.size() == 0){dp[i] = dp[i-1] + a[i]; path[i] = i;}
      else {
        int p = 0;
        for(int j=1;j<(int)v.size();j++) {
          if(dp[v[j]-1] + (mul[i] / mul[v[j]-1]) > dp[v[p]-1] + (mul[i] / mul[v[p]-1]) )
            p = j;
        }
        if(dp[v[p]-1] + (mul[i] / mul[v[p]-1]) >= dp[i-1] + a[i]) {
          dp[i] = dp[v[p]-1] + (mul[i] / mul[v[p]-1]);
          path[i] = v[p];
        }else {
          dp[i] = dp[i-1] + a[i];
          path[i] = i;
        }
      }
      v.push_back(i);
    }
  }

  // for(int i=l;i<=r;i++) printf("%lld ",dp[i]); printf("\n");
  // for(int i=l;i<=r;i++) printf("%lld ",path[i]); puts("");

  int pos = r;
  while(pos >= l) {
    int pre = path[pos];
    for(int j=pre;j<pos;j++) ans[j] = '*';
    ans[pre-1] = '+';
    pos = pre - 1;
  }
}

void solve() {

  int cnt = 0,lst = 1;
  for(int i=1;i<=n+1;i++) {
    if(a[i] == 0) {
      ans[i-1] = ans[i] = '+';
      if(cnt > 28) {
        int l = lst; int r = i-1;
        while(a[l] == 1 && l <= r) ans[l] = '+',l++;
        while(a[r] == 1 && l <= r) ans[r-1] = '+',r--;

        for(int j=l;j<r;j++) ans[j] = '*'; 
      }
      else solve(lst , i-1);
      cnt = 0 , lst = i+1;
    }
    else if(a[i] > 1){
      cnt++;
    }
  }

  wr();
}

signed main() {
  n = rd();
  for(int i=1;i<=n;i++) a[i] = rd();
  scanf("%s",s+1); len = strlen(s+1);
  if(len == 1) {
    memset(ans,s[len],sizeof(ans));
    wr(); return 0;
  }else if(check('+') && check('*')) solve();
  else if(check('-') && check('*')) {
    int pos = n+1;
    for(int i=2;i<=n;i++) if(a[i] == 0){pos = i; break;}
    memset(ans,'*',sizeof(ans));
    if(pos == n+1) {
      wr(); return 0;
    }else {
      for(int i=1;i<pos-1;i++) ans[i] = '*';
      ans[pos-1] = '-';
      for(int i=pos;i<n;i++) ans[i] = '*';
      wr(); return 0;
    }
  }else if(check('+') && check('-')) {
    memset(ans,'+',sizeof(ans));
    for(int i=1;i<n;i++) printf("%lld%c",a[i],ans[i]);
    printf("%lld\n",a[n]);
  }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值