BZOJ5324 JXOI2018守卫(区间dp)

  对于每个区间[l,r],显然右端点r是必须放置守卫的。考虑其不能监视到的点,构成一段段区间。一个非常显然但我就是想不到的性质是,对于这样的某个区间[x,y],在(y+1,r)内的点都是不能监视到这个区间内的任何一点的,证明考虑一下斜率之间的关系即可。于是该区间的最右一个守卫可以放置在y,也可以放置在y+1,这样可以得到一个显然的区间dp,暴力dp是O(n3)的,固定右端点后移动左端点同时记录答案就可以优化到O(n2)。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 5010
#define inf 1000000010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
int n,a[N],f[N][N],tot;
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj5324.in","r",stdin);
    freopen("bzoj5324.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    n=read();
    for (int i=1;i<=n;i++) a[i]=read();
    memset(f,42,sizeof(f));
    for (int i=1;i<=n;i++)
    {
        double k=inf;
        int ans=1;f[i][i]=1;tot^=1;
        for (int j=i-1;j;j--)
        if ((double)(a[i]-a[j])/(i-j)<k) f[j][i]=ans,tot^=ans,k=(double)(a[i]-a[j])/(i-j);
        else
        {
            int x=j;
            while (x>1&&(double)(a[i]-a[x-1])/(i-x+1)>=k) x--;
            for (int k=j;k>=x;k--) tot^=f[k][i]=ans+min(f[k][j],f[k][j+1]);
            ans+=min(f[x][j],f[x][j+1]);j=x;
        }
    }
    cout<<tot;
    return 0;
}

 

posted @ 2018-12-07 00:24  Gloid  阅读(145)  评论(0编辑  收藏  举报