[JXOI2018]守卫

题意

link
\(n(\neq 5 \times 10^3)\) 个数 \(h_i\), 每个点可以放守卫,能覆盖他向左能看到的点,能看到定义为两点连线没有经过其它点即以下的位置。

求所有区间最小守卫数的异或和。

区间 dp

状态

\(f[l][r]\) 表示区间最小的守卫数。

初始状态

注意到区间最右端一定要放一个守卫。

\(f[r][r] = 1\)

转移

考虑枚举目前区间 \([l, r]\) r 能看见的最左边的点 \(p\), 那么 \(p - 1\) 就是看不到的,因此再要 \(p - 1\)\(p\) 处放守卫,也就是子区间 \(f(l, p), f(l, p - 1)\) 的子问题,

对于 \([p + 1, r]\) 也是一个子问题。

\(f[l][r] = \min_p\{f[l][p], f[l][p - 1]\} + f[p + 1][r]\)

分析

区间dp 状态 \(n ^ 2\), 转移可以做到 \(O(1)\)

那么时间复杂度就是 \(O(n ^ 2)\)

代码

这个 sum 就是 \(f[p + 1][r]\) 只不过是顺路更新。

#include<bits/stdc++.h>

using namespace std;

//using ll = long long;
const int MAXN = 5010;
const int INF = 0x7fffffff;
const int mod = 1000000007;
const double eps = 1e-9; 

template <typename T>
void Read(T &x) {
	x = 0; T f = 1; char a = getchar();
	for(; a < '0' || '9' < a; a = getchar()) if (a == '-') f = -f;
	for(; '0' <= a && a <= '9'; a = getchar()) x = (x * 10) + (a ^ 48);
	x *= f;
}

inline void add(int &a, const int &b) { 
	a = a + b;
	if (a >= mod) a -= mod;
	if (a < 0) a += mod;  
} 
inline int mul(const int &a, const int &b) {
	return 1ll * a * b % mod; 
}
int qpow(int a, int b) {
	int sum(1);
	while(b) {
		if (b & 1) sum = mul(sum, a);
		a = mul(a, a);
		b >>= 1;
	}
	return sum; 
}

int n; 
int h[MAXN], f[MAXN][MAXN];

double slope(int a, int b, int c, int d) {
	return 1.0 * (a - c) / (b - d); 
}

int main() {
	cin >> n;
	for (int i = 1; i <= n; i ++) {
		cin >> h[i]; 
	}
	int ans = 0; 
	for (int r = 1; r <= n; r ++) {
		f[r][r] = 1;
		ans ^= 1;
		int sum = 1, p = 0; 
		for (int l = r - 1; l >= 1; l --) {
			if (!p || slope(h[r], r, h[p], p) > slope(h[r], r, h[l], l)) {
				if (p) sum += min(f[l + 1][p], f[l + 1][p - 1]);
				p = l;
			} 
			
			f[l][r] = sum + min(f[l][p], f[l][p - 1]);
			ans ^= f[l][r]; 
		}
	}
	cout << ans << endl;
	return 0;
} 
posted @ 2022-02-28 20:42  qjbqjb  阅读(22)  评论(0编辑  收藏  举报