洛谷P4563 [JXOI2018]守卫(dp)
程序员文章站
2022-04-09 19:01:45
题意 "题目链接" Sol 非常有意思的题目。 我们设$f[l][r]$表示区间$[l,r]$的答案。 显然$r$位置一定有一个保镖 同时不难观察到一个性质:拿$[1, n]$来说,设其观察不到的某个区间为$[l_k, r_k]$,那么$r_k$与$r_k + 1$一定有一个保镖,而且每段区间的贡献 ......
题意
sol
非常有意思的题目。
我们设\(f[l][r]\)表示区间\([l,r]\)的答案。
显然\(r\)位置一定有一个保镖
同时不难观察到一个性质:拿\([1, n]\)来说,设其观察不到的某个区间为\([l_k, r_k]\),那么\(r_k\)与\(r_k + 1\)一定有一个保镖,而且每段区间的贡献都是独立的。
这样我们可以预处理出任意两个点是否能看见(直接记上一个能看到的位置然后比较斜率)。
然后直接枚举\(r\),不断把\(l\)往左移动更新答案,这样可以保证更新的时候中间的区间都计算过,可以直接前缀和优化
复杂度\(o(n^2)\)
#include<bits/stdc++.h> #define fin(x) freopen(#x".in", "r", stdin); using namespace std; const int maxn = 5001; template<typename a, typename b> inline bool chmax(a &x, b y) {return x < y ? x = y, 1 : 0;} template<typename a, typename b> inline bool chmin(a &x, b y) {return x > y ? x = y, 1 : 0;} inline int read() { char c = getchar(); int x = 0, f = 1; while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } int n, f[maxn][maxn]; double a[maxn]; bool can[maxn][maxn]; int main() { //fin(a); n = read(); for(int i = 1; i <= n; i++) a[i] = read(); for(int i = 1; i <= n; i++) { for(int j = i - 1, las = -1; j; j--) if((las == -1) || (1ll * (a[i] - a[las]) * (i - j) > 1ll * (a[i] - a[j]) * (i - las))) can[i][j] = can[j][i] =1, las = j; } int ans= 0; for(int i = 1; i <= n; i++) { int s = 1, las = 0; for(int j = i; j; j--) { if(can[j][i]) { if(!can[j + 1][i]) s += min(f[j + 1][las], f[j + 1][las + 1]); f[j][i] = s; } else { if(can[j + 1][i]) las = j; f[j][i] = s + min(f[j][las], f[j][las + 1]); } ans ^= f[j][i]; // printf("%d ", f[j][i]); } // puts(""); } cout << ans; return 0; }
上一篇: vue2.0 实现全选和全不选