题目
思路
一开始感觉是凸包啥的……然后惨痛爆零……
对于一个区间,发现必须有一个守卫(否则就没法监视号亭子)。
然后能看到一些亭子。剩下一些亭子。
红色的就是看不到的亭子(被右边的蓝色亭子挡住了)。蓝色的是看的到的。
由于守卫可以 随意抬头但不能低头,所以能够看到多少,就取决于 能看到多低。
很明显,号亭子看不到的,红色亭子也看不到,因为绿色的线比红色的线低。
所以每两个看的到的亭子之间的那一段区间,是 相互独立 的。
于是使用区间。对于某个固定的右端点,左端点不断左移,可以维护能够看到的最矮的(也就是最左边的、能看到的)。那么就可以做到转移了。总复杂度。
转移时要注意,由于最近的蓝色亭子是可以被看到的,要考虑是否在蓝色亭子安排守卫。
代码
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
const int MaxN = 5000;
int dp[MaxN][MaxN], n, h[MaxN];
int main(){
scanf("%d",&n);
for(int i=0; i<n; ++i)
scanf("%d",&h[i]);
int ans = 1; // when i equals to zero
for(int i=1; i<n; ++i){
dp[i][i] = dp[i-1][i] = 1;
int wall = i-1; // 上一个蓝色亭子
for(int j=i-2; ~j; --j)
if((0ll+h[i]-h[wall])*(wall-j) > (0ll+h[wall]-h[j])*(i-wall)){
dp[j][i] = dp[j+1][i];
wall = j;
} // j号亭子可见,无需考虑j号亭子,只需保证能看到[j+1,i]
else
dp[j][i] = dp[wall][i]+min(dp[j][wall],dp[j][wall-1]);
// dp[j][wall]:在蓝色亭子处安排守卫
}
for(int i=0; i<n; ++i)
for(int j=i; j<n; ++j)
ans ^= dp[i][j];
printf("%d",ans);
return 0;
}
来源:https://blog.csdn.net/qq_42101694/article/details/102761127