高中OJ3733. 【Usaco2014Open银组】照相 (非线段树算法)

这题可以用线段树去做,但是比较麻烦。


根据题目可以知道,当一个序列可以成立时,该序列长度必须为偶数且该序列中W的个数要不小于S的个数。
∵总W个数-左W个数-右W个数≥总S个数-左S个数-右S个数
∴总差-左差-右差≥0
∴总差-右差≥左差
右边的差很好求,只要从后往前累加一个前缀和。
为了让结果最大,所以就要尽量往左扩展。
根据上面的公式,设sum[i]表示从左第1位开始累加前缀和小于等于i时最靠左的边界位置。
很显然,一次就可以算出所有的sum[i]。

接着枚举右侧的差,累加一个和,用总和减去右侧的和即为左侧的和。
再套入sum,可以得出最靠左的左边界位置。

但是这种算法没有考虑到一个问题——奇偶性。
比如说,我们当前找到的长度已经大于最大长度,但是由于长度为奇数,所以无法算入答案。
但是有可能此时把左边界向右移一段,虽然长度没有原来那么长,但长度仍然比之前算出的最大长度要大,而且符合条件。

为了考虑到这个问题,在设sum时要分为两种情况来设,计算时也要分别计算。

var
        a:array[1..100000,1..2] of longint;
        s:array[-100000..100000,0..1] of longint;
        sum:array[-100001..100000,0..1] of longint;
        n,i,j,k,l,tot,max,r:longint;
        ch:char;
procedure swap(var x,y:longint);
var
        z:longint;
begin
        z:=x;
        x:=y;
        y:=z;
end;
procedure qsort(l,r:longint);
var
        i,j,mid:longint;
begin
        i:=l;
        j:=r;
        mid:=a[(l+r) div 2,1];
        while i<=j do
        begin
                while (a[i,1]<mid) do inc(i);
                while (a[j,1]>mid) do dec(j);
                if i<=j then
                begin
                        swap(a[i,1],a[j,1]);
                        swap(a[i,2],a[j,2]);
                        inc(i);
                        dec(j);
                end;
        end;
        if l<j then qsort(l,j);
        if i<r then qsort(i,r);
end;
begin
        assign(Input,'pairphoto.in'); reset(Input);
        assign(Output,'pairphoto.out'); rewrite(Output);
        readln(n);
        for i:=1 to n do
        begin
                readln(a[i,1],ch,ch);
                if ch='W' then
                a[i,2]:=1
                else
                a[i,2]:=-1;
                tot:=tot+a[i,2];
        end;
        qsort(1,n);
        for i:=1 to n do
        begin
                j:=j+a[i,2];
                if s[j,i mod 2]=0 then
                s[j,i mod 2]:=i;
        end;
        sum[-n-1,0]:=n*2+1;
        sum[-n-1,1]:=n*2+1;
        for i:=-n to n do
        begin
                sum[i]:=sum[i-1];
                if (s[i,0]>0) and (sum[i,0]>s[i,0]) then
                sum[i,0]:=s[i,0];
                if (s[i,1]>0) and (sum[i,1]>s[i,1]) then
                sum[i,1]:=s[i,1]; //前缀和
        end;
        j:=0;
        for r:=n downto 1 do
        begin
                l:=sum[tot-j,0];
                if (l<=r) and (a[r,1]-a[l,1]>max) and ((r-l+1) mod 2=0) then
                max:=a[r,1]-a[l,1];
                l:=sum[tot-j,1];
                if (l<=r) and (a[r,1]-a[l,1]>max) and ((r-l+1) mod 2=0) then
                max:=a[r,1]-a[l,1];
                j:=j+a[r,2]; //分类讨论
       end;
        writeln(max);
        close(Input); close(Output);
end.
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值