[洛谷P4563]守卫

痞子三分冷 提交于 2019-12-02 12:52:52

题目

传送门

思路

一开始感觉是凸包啥的……然后惨痛爆零……
对于一个区间[l,r][l,r],发现rr必须有一个守卫(否则就没法监视rr号亭子)。
然后rr能看到一些亭子。剩下一些亭子。

在这里插入图片描述

红色的就是看不到的亭子(被右边的蓝色亭子挡住了)。蓝色的是看的到的。
由于守卫可以 随意抬头但不能低头,所以能够看到多少,就取决于 能看到多低
很明显,rr号亭子看不到的,红色亭子也看不到,因为绿色的线比红色的线低。
所以每两个看的到的亭子之间的那一段区间,是 相互独立 的。
于是使用区间DP\mathbb{DP}。对于某个固定的右端点,左端点不断左移,可以O(1)O(1)维护rr能够看到的最矮的(也就是最左边的、能看到的)。那么就可以做到O(1)O(1)转移了。总复杂度O(n2)O(n^2)
转移时要注意,由于最近的蓝色亭子是可以被看到的,要考虑是否在蓝色亭子安排守卫。

代码

#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;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!