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;
}

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com