zoukankan      html  css  js  c++  java
  • 【JXOI2018】守卫

    题面 

    https://www.luogu.org/problem/P4563

    题解

    一步杀神仙题。

    首先,对于一段区间$[l..r]$,$r$必须选。

    $r$选以后,能被看到的元素必然是和$r$斜率的后缀最小值。

    根据$mbox{Gloid}$爷的神仙性质,对于一段不能被看到的区间$[x..y]$(后缀最小值在$y+1$处),不可以被$(y+1..r]$的守卫看到。

    我们来感性理解一下这个结论,即对于$[x..y]$中任意一点$a$,我们把$a$和$y+1$连线, 没有点在这条连线上方。

    若某个点$b(y+1<b<r)$在连线上方,则$r$就看不到$y+1$了。

    所以我们知道,要解决$[x..y]$,只能在$y$或$y+1$处放守卫。

    $f[l][r]$代表区间$[l..r]$的答案(在$r$处必放守卫)

    设$p$为当前最近的一个后缀最小值,显然有转移:

    $$f[l][r]=min(f[l][p],f[l][p-1])+f[p][r]$$

    固定$r$,反求$l$即可。

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ri register int
    #define N 5050
    using namespace std;
    
    int sum;
    int n,h[N];
    int f[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 (ri i=1;i<=n;i++) scanf("%d",&h[i]);
      for (ri r=1;r<=n;r++) {
          f[r][r]=1;
          ans^=f[r][r];
          int sum=1,p=0;
          for (ri l=r-1;l>=1;l--) {
            if (!p || cansee(l,p,r)) sum=f[l+1][r],p=l;
            f[l][r]=sum+min(f[l][p-1],f[l][p]);
            ans^=f[l][r];
          }
      }
      cout<<ans<<endl;
    }
  • 相关阅读:
    弹性盒模型的实际应用
    大图滚动--这是精髓实例
    三级联动
    sql
    jsp2
    marquee
    人机五子棋(AI算法有瑕疵)
    Jsp1
    倒计时
    时间
  • 原文地址:https://www.cnblogs.com/shxnb666/p/11794264.html
Copyright © 2011-2022 走看看