欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

洛谷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;
}