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