把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

luogu P4056 [JSOI2009]火星藏宝图

题面传送门
显然这道题有定义\(dp_i\)表示到\(i\)点的最大价值,\(dp\)\(dp_{i}=\max\limits_{j=1}^{i-1}{dp_j-(x_i-x_j)^2-(y_i-y_j)^2+w_i}\)这样的\(dp\)\(O(n^2)\)
考虑怎么优化,显然一列中只有最下面的列转移更优,这样复杂度变成\(O(nm)\)
\(g_i\)为当前\(i\)列能转移的最优答案。\(now\)为当前行,\(dis(i,j)=(i-j)^2\)则有\(dp_i=dp_{g_j}+(i-j)^2+dis(now,g_j)+w_i\)
看上去可以斜优。
\(k<j\)\(k\)劣于\(j\),有\(dp_{g_j}-(i-j)^2-dis(now,g_j)>dp_{g_k}-(i-k)^2-dis(now,g_k)\)
化简,得到\(dp_{g_j}-j^2+2ij-dis(now,g_j)>dp_{g_k}-k^2+2ik-dis(now,g_k)\)
\(dp_{g_j}-dp_{g_k}-j^2+k^2-dis(now,g_j)+dis(now,g_k)>2i(k-j)\)
\(\frac{dp_{g_j}-dp_{g_k}-j^2+k^2-dis(now,g_j)+dis(now,g_k)}{k-j}<2i\)
就可以维护单调上升的队列了。
代码实现:

#include<cstdio>
#define X(a) (dp[g[a]][a]-a*a-dis(z,g[a]))
#define Y(a) (a)
using namespace std;
int n,m,k,x,y,z,w[1039][1039],dp[1039][1039],g[1039],q[1039],head,tail;
inline int dis(int x,int y){return  (x-y)*(x-y);}
inline double slope(int x,int y,int z){return (Y(y)-Y(x)==0)?-1e9:(X(x)-X(y))*1.0/(Y(y)-Y(x));}
int main(){
	//freopen("1.in","r",stdin);
	register int i,j,k;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++){
		scanf("%d%d%d",&x,&y,&z);w[x][y]=z;
	}
	dp[1][1]=w[1][1];g[1]=1;w[1][1]=0;
	for(i=1;i<=m;i++){
		head=1;tail=0;
		for(j=1;j<=m;j++){
			if(g[j]){
				while(head<tail&&slope(q[tail-1],q[tail],i)>slope(q[tail],j,i))tail--;
				q[++tail]=j;
			} 
			if(w[i][j]){
				while(head<tail&&slope(q[head],q[head+1],i)<2*j) head++;
				k=q[head];dp[i][j]=dp[g[k]][k]-dis(j,k)-dis(i,g[k])+w[i][j];g[j]=i;
				while(head<tail&&slope(q[tail-1],q[tail],i)>slope(q[tail],j,i))tail--;
				q[++tail]=j;
			}
		//	printf("%d ",dp[i][j]);
		}
	//	putchar('\n');
	}
	printf("%d\n",dp[m][m]);
} 
posted @ 2021-01-01 19:19  275307894a  阅读(78)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end