[JXOI2018]守卫

题意

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

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

区间 dp

状态

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

初始状态

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

f[r][r]=1

转移

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

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

f[l][r]=minp{f[l][p],f[l][p1]}+f[p+1][r]

分析

区间dp 状态 n2, 转移可以做到 O(1)

那么时间复杂度就是 O(n2)

代码

这个 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 @   qjbqjb  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示