JXOI2018 守卫
听说这题误导性很强……我倒没有…… 我只是单纯的不知道怎么做……
实际上很简单。考虑到最右面是必须放守卫的,我们从右边开始DP,设\(dp[l][r]\)表示区间\([l,r]\)需要摆放的最小守卫数,p表示能观察到的最远的亭子,那么有\(dp[l][r] = sum + min(dp[l][p],dp[l][p-1])\),因为你在p或者p-1必须要放一个。
sum的更新是每次如果p被更新的话,那么\(sum += min(dp[l][p],dp[l][p-1])\)
然后我们这样从左往右枚举右端点,然后对于每个右端点倒着DP回去就行了。顺便亦或一下答案。
#include<bits/stdc++.h>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')
#define pr pair<int,int>
#define mp make_pair
#define fi first
#define sc second
using namespace std;
typedef long long ll;
const int M = 5005;
const int N = 10000005;
int read()
{
int ans = 0,op = 1;char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') op = -1;ch = getchar();}
while(ch >='0' && ch <= '9') ans = ans * 10 + ch - '0',ch = getchar();
return ans * op;
}
struct dot
{
double x,y;
}a[M];
double slope(int f,int g){return (a[g].y - a[f].y) / (a[g].x - a[f].x);}
int n,dp[M][M],ans,sum,p;
int main()
{
n = read();
rep(i,1,n) a[i].x = i,scanf("%lf",&a[i].y);
rep(r,1,n)
{
dp[r][r] = 1,sum = 1,p = 0;
per(l,r-1,1)
{
if(!p || slope(l,r) < slope(p,r)) sum += min(dp[l+1][p],dp[l+1][p-1]),p = l;
dp[l][r] = sum + min(dp[l][p],dp[l][p-1]);
}
}
rep(i,1,n)rep(j,1,i) ans ^= dp[j][i];
printf("%d\n",ans);
return 0;
}
当你意识到,每个上一秒都成为永恒。