鏖战字符串

题目描述
Abwad在nbc即将完成她的程序的时候,急中生智拔掉了她电脑的电源线,争取到了宝贵的时间。作为著名论文《论Ctrl-C与Ctrl-V在信息学竞赛中的应用》的作者,他巧妙地使用了这种上古秘术,顺利扳回一城。
在决胜局中,Abwad决定和nbc鏖战字符串,比的是谁能更快地将一个“量子态的字符串”删除。“量子态的字符串”的每个字符都有一个删除难度dif[i]。“量子态的字符串”非常顽固,只能先分割成若干个子串,然后再通过以下两种方式删除:
1、假设子串的所有字符的删除难度之和为x,消耗a*x^2+b的时间可以将子串扔进回收站。
2、若子串中出现次数最多的字符出现的次数不少于l次且不多于r次,那么采用“量子态的py自动机”算法可以消耗c*x+d的时间将子串扔进回收站。
Abwad自然知道最少用多少时间就能将字符串删去,因此,他希望你求出删去每个前缀[1,i]的最少用时。

输入
第一行七个整数n,a,b,c,d,l,r,其中n表示字符串的长度
第二行一行一个长度为n的字符串
第三行一行n个整数,表示每个字符的删除难度dif[i]
输出
n行,每行一个整数ans,表示删去前缀[1,i]最短的时间
样例输入
5 1 3 1 5 1 1
abwad
1 1 1 1 1
样例输出
4
7
8
12
13

提示
【样例1解释】
以前缀[1,n]为例,将串分为a、bwad两个子串,用方法1删去第一个子串,用方法2删去第二个子串,用时1*1+3+1*4+5=13
对于所有的数据,满足n≤100000,1≤a,b,c,d≤233,1≤l,r≤n,dif[i]≤50,所有字符由小写字母组成。
【后记】
在Abwad和nbc同时将最后一个子串删去时,一个带着黑色方框眼镜,方脸,穿着高腰裤的长者,乘着圣洁的祥云,飞进了YYHS的机房。在他伟大的思想的启发下,Abwad和nbc终于放下了对名利的追逐,找到了人生的意义——吃吃吃。从此,他们过上了幸福快乐的生活……

对于方式一,显然是斜率优化。至于方案二,dp[i]=min{dp[j]+(sum[i]-sum[j])*c+d},变形可得,dp[i]=dp[i]*c+d+min(dp[j]-sum[j]*c)。由于方案二需要满足“最多的字符出现的次数不少于l次且不多于r”,所以必定是在一个区间内转移的,所以我们维护这样一个区间的单调队列,记录min(dp[j]-sum[j]*c),这样就可以转移了。

var
n,l,r,i,j,markl,st,ed,markr,sta,eda:longint;
a,b,c,d,x:int64;
sum,f,dif,q,p:array[0..500022] of int64;
s:array[0..500022] of char;
cntl,cntr:array['a'..'z'] of longint;
function min(a,b:int64):int64;
begin
  if a<b then exit(a) else exit(b);
end;

function judge1(i,j,k:longint):boolean;
begin
  if f[j]+a*sqr(sum[j])-f[k]-a*sqr(sum[k])<=2*a*sum[i]*(sum[j]-sum[k]) then exit(true)
                                                                              else exit(false);
end;

function judge2(i,j,k:longint):boolean;
var
aa,b,c,d:int64;
begin
  aa:=f[i]+a*sqr(sum[i])-f[j]-a*sqr(sum[j]);
  b:=(sum[i]-sum[j]);
  c:=f[j]+a*sqr(sum[j])-f[k]-a*sqr(sum[k]);
  d:=(sum[j]-sum[k]);
  if aa*d<=b*c then exit(true)
               else exit(false);
end;

begin
  readln(n,a,b,c,d,l,r);
  for i:=1 to n do
    read(s[i]);
  readln;
  for i:=1 to n do
  begin
    read(dif[i]);
    sum[i]:=sum[i-1]+dif[i];
  end;
  st:=1;
  ed:=1;
  sta:=1;
  eda:=0;
  markl:=1;
  markr:=0;
  for i:=1 to n do
  begin
    while (st<ed)and(judge1(i,q[st+1],q[st])) do st:=st+1;
    f[i]:=f[q[st]]+b+a*sqr(sum[i]-sum[q[st]]);
    inc(cntl[s[i]]);
    inc(cntr[s[i]]);
    while cntl[s[i]]>r do
    begin
      dec(cntl[s[markl]]);
      markl:=markl+1;
      while (sta<=eda)and(p[sta]+1<markl) do inc(sta);
    end;
    while cntr[s[i]]>=l do
    begin
      if markr>0 then
      begin
        dec(cntr[s[markr]]);
        if cntr[s[i]]<l then
        begin
          inc(cntr[s[markr]]);
          break;
        end;
      end;
      while (sta<=eda)and(f[markr]-sum[markr]*c<=f[p[eda]]-sum[p[eda]]*c) do dec(eda);
      eda:=eda+1;
      p[eda]:=markr;
      markr:=markr+1;
    end;
    if sta<=eda then f[i]:=min(f[i],f[p[sta]]+d+(sum[i]-sum[p[sta]])*c);
    writeln(f[i]);
    while (st<ed)and(judge2(i,q[ed],q[ed-1])) do ed:=ed-1;
    ed:=ed+1;
    q[ed]:=i;
  end;
end.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值