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

posted @ 2019-03-25 21:35  CaptainLi  阅读(208)  评论(0编辑  收藏  举报