题解 P4056 【[JSOI2009]火星藏宝图】
https://sakits.com/bzoj1560/
题解
这题网上题解大都是
首先先说一下
朴素的转移当然是
所以对于每一列我们只需要保存可转移点中行数最大的点,从这些点转移就好了,那么转移复杂度是
观察一下方程,平方展开后有一些
设
设
然后就可以斜率优化了...注意横坐标相同时斜率要设为-inf
代码
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=1010, inf=1e9;
const double eps=1e-6;
int n, m, x, y, z;
int f[maxn][maxn], w[maxn][maxn], pos[maxn], dis[maxn], q[maxn];
inline void read(int &k)
{
int f=1; k=0; char c=getchar();
while(c<'0' || c>'9') c=='-' && (f=-1), c=getchar();
while(c<='9' && c>='0') k=k*10+c-'0', c=getchar();
k*=f;
}
inline double xl(int x, int y){return (x==y)?-inf:1.0*(f[pos[x]][x]-f[pos[y]][y]-dis[x]+dis[y]-x*x+y*y)/2/(y-x);}
int main()
{
read(n); read(m);
for(int i=1;i<=n;i++) read(x), read(y), read(w[x][y]);
memset(f, 200, sizeof(f));
f[1][1]=w[1][1]; pos[1]=1; w[1][1]=0;
for(int i=1;i<=m;i++)
{
for(int j=1;j<=m;j++) dis[j]=(pos[j]!=0)*(pos[j]-i)*(pos[j]-i);
int l=1, r=0;
for(int j=1;j<=m;j++)
{
if(pos[j])
{
while(l<r && xl(q[r-1], q[r])>xl(q[r], j)-eps) r--;
q[++r]=j;
}
if(w[i][j])
{
while(l<r && xl(q[l], q[l+1])-eps<j) l++;
f[i][j]=f[pos[q[l]]][q[l]]-dis[q[l]]-(q[l]-j)*(q[l]-j)+w[i][j];
pos[j]=i; dis[j]=0;
while(l<r && xl(q[r-1], q[r])>xl(q[r], j)-eps) r--;
q[++r]=j;
}
}
}
printf("%d\n", f[m][m]);
}