1 条题解

  • 0
    @ 2025-8-24 22:01:24

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Ebola
    来证个定理吧!

    搬运于2025-08-24 22:01:24,当前版本为作者最后更新于2018-10-20 11:59:42,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    本题的误导性极强,看到题大部分人都会去想凸包、单调栈一类的东西

    显然的有这么一个事实:若游戏的区间是[l,r][l, r],那么rr号亭子必然需要放一个保镖

    于是我们确定右端点,然后可以从右往左扫,记pp表示rr号亭子能看到的最左边的亭子。然后扫的过程中,用一个sum记录p及其右边部分的答案。因为r最左端能看到的点是pp,所以p1p-1的纵坐标必然低于pp,所以对于[l,p1][l, p-1]这一段,我们必然要在ppp1p-1中选一个放置保镖,故有:

    fl,r=sum+min(fl,p1,fl,p)f_{l,r}=sum+\min(f_{l,p-1},f_{l,p})

    在扫的过程中,ppsumsum也是需要更新的,若当前点是rr号亭子可见的,那么必然要在之前的ppp1p-1中选一个放置保镖,所以sumsum要加上min(fl+1,p1,fl+1,p)\min(f_{l+1,p-1},f_{l+1,p}),然后将pp更新至当前的ll

    考虑可见性判定,显然只要llrr的连线,比pprr的连线斜率更大,ll就是可见的

    就这样,做完了……如果想的方向对了真的非常简单

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N=5010;
    int f[N][N],h[N],n;
    
    double slope(int l,int r){return (double)(h[r]-h[l])/(r-l);}
    bool cansee(int l,int x,int r){return slope(x,r)>slope(l,r);}
    
    int main()
    {
        int ans=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",h+i);
        for(int r=1;r<=n;r++)
        {
            ans^=(f[r][r]=1);
            int sum=1,p=0;
            for(int l=r-1;l>=1;l--)
            {
                if(!p||cansee(l,p,r)) sum+=min(f[l+1][p-1],f[l+1][p]),p=l;
                ans^=(f[l][r]=sum+min(f[l][p-1],f[l][p]));
            }
        }
        cout<<ans<<endl;
        return 0;
    }
    
    • 1

    信息

    ID
    3496
    时间
    1000ms
    内存
    500MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者