题目
思路
这道题的题面十分具有迷惑性。。。
某位大犇上来直接用凸包
但是仔细分析之后发现,有点像区间DP
对于\(l_0r_0\) 和\(l_1 r_1\)两个区间,如果这两个区间是不相交
那么这两个区间实际上是不会影响对方的DP值的
有了这个结论之后,我们设\(dp_{i j}\)为区间为i和j的最小值
转移方程即为\(dp_{ij}=1+\sum _{}dp_{l_{k}r_k}\)
50pts
一个时间复杂度为\(O(n^3)\)算法就出现了
前两层循环枚举左端点与右端点,
最内层的循环就是枚举之间看不到的区间
对于每一个看不到的区间可能在它的右端点放一个守卫可能更
100pts
我们考虑对50pts的算法的改进,发现最内层的k是根本不需要的
因为可以先固定右端点,
在枚举左端点的时候顺便就可以将看不见的统计出来
代码
#include<iostream>
#include<climits>
using namespace std;
#define int long long
int n;
int h[5005];
int ans=1;
int dp[5005][5005];//表示区间i,j的最小值
bool f[5005][5005];//表示i号节点和j号节点之间能否互相看到
double solve_slope(int a,int b)
{
return 1.0*(h[a]-h[b])/(a-b);
}
signed main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>h[i];
for(int i=2;i<=n;i++)
{
double now=-INT_MIN;
for(int j=i-1;j>=1;j--)
{
double t=solve_slope(j,i);
if(t>now)
{
f[j][i]=f[i][j]=1;
now=t;
}
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
cout<<f[i][j]<<" ";
cout<<endl;
}
for(int i=1;i<=n;i++)
{
int lastt=0;
int s=1;
for(int j=i;j>=1;j--)
{
if(f[j][i])
{
if(!f[j+1][i])
{
s+=min(dp[j+1][lastt],dp[j+1][lastt+1]);
}
dp[j][i]=s;
}
else
{
if(f[j+1][i])
{
lastt=j;
}
dp[j][i]=s+min(dp[j][lastt+1],dp[j][lastt]);
}
ans^=dp[j][i];
}
}
cout<<ans;
return 0;
}