【BZOJ5324】守卫(JXOI2018)-区间DP+优化

测试地址:守卫
做法:本题需要用到区间DP+优化。
看到数据范围,容易想到令 f(l,r) f ( l , r ) 为区间 [l,r] [ l , r ] 的答案,我们来考虑怎么转移。
对于一个区间 [l,r] [ l , r ] ,首先点 r r 是一定要有人的,对于点r,它能看到的所有点可以这样求:从点 r1 r − 1 开始,如果它到点 r r 的斜率和上一个能看到的点到点r的斜率相比更小,那么当前点就能看到,否则就看不到(可以把坐标系转换为以点 r r 为极点的极坐标系来考虑),这样从右往左扫一遍就可以求出它能看到的所有点了。于是我们可以O(n2)预处理出这些信息。
那我们有了这些信息,再考虑每一个点 r r 看不到的点的连续区间[lk,rk],注意到对于 x>rk+1 x > r k + 1 ,点 x x 都是不可能看到区间[lk,rk]中的点的,这个结论画画图也可以得出来。由这个结论我们可以得出,每个这样的区间的决策对整个区间 [l,r] [ l , r ] 来说,贡献是独立的,而对于一个区间 [lk,rk] [ l k , r k ] ,要使得里面的点全部被看到,可以选择在点 rk r k 或点 rk+1 r k + 1 布置一个人,贡献分别为 f(lk,rk) f ( l k , r k ) f(lk,rk+1) f ( l k , r k + 1 ) ,因此我们有状态转移方程:
f(l,r)=1+min(f(lk,rk),f(lk,rk+1)) f ( l , r ) = 1 + ∑ min ( f ( l k , r k ) , f ( l k , r k + 1 ) )
这个方程是 O(n3) O ( n 3 ) 的,显然不能通过此题,这就需要我们的优化。从方程本身的角度已经很难优化下去了,因此我们对求方程的方法进行优化。
注意到 rk+1<r r k + 1 < r ,因此我们从小到大枚举 r r 来确保当前状态所需要的状态都已经被计算,而在r固定的情况下, l l 左移时,经过的r看不到的连续区间的贡献就可以顺便记录下来,这样我们就可以做到 O(n2) O ( n 2 ) 的总时间复杂度了,于是我们就完成了这一题。
以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
ll n,h[5010],f[5010][5010];
bool see[5010][5010];

int main()
{
    scanf("%lld",&n);
    for(ll i=1;i<=n;i++)
        scanf("%lld",&h[i]);

    for(ll i=1;i<=n;i++)
    {
        see[i][i]=0;
        for(ll j=i-1,last=0;j>=1;j--)
        {
            if (!last||(h[i]-h[j])*(i-last)<(h[i]-h[last])*(i-j))
            {
                see[i][j]=1;
                last=j;
            }
            else see[i][j]=0;
        }
    }

    ll ans=0;
    for(ll r=1;r<=n;r++)
    {
        ll lastans=1,lastr=0;
        for(ll l=r;l>=1;l--)
        {
            if (see[r][l])
            {
                if (lastr)
                {
                    lastans+=min(f[l+1][lastr],f[l+1][lastr+1]);
                    lastr=0;
                }
            }
            else
            {
                if (!lastr) lastr=l;
                f[l][r]+=min(f[l][lastr],f[l][lastr+1]);
            }
            f[l][r]+=lastans;
            ans^=f[l][r];
        }
    }
    printf("%lld",ans);

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值