洛谷3195 [HNOI2008]玩具装箱TOY(斜率优化+dp)

qwq斜率优化好题

第一步还是考虑最朴素的 d p dp dp

d p = d p [ j ] + ( i − j − 1 + s u m [ i ] − s u m [ j ] ) 2 dp=dp[j]+(i-j-1+sum[i]-sum[j])^2 dp=dp[j]+(ij1+sum[i]sum[j])2

f [ i ] = s u m [ i ] + i f[i]=sum[i]+i f[i]=sum[i]+i

那么考虑将上述柿子变成 d p [ i ] = d p [ j ] + ( f [ i ] − f [ j ] − 1 − l ) 2 dp[i]=dp[j]+(f[i]-f[j]-1-l)^2 dp[i]=dp[j]+(f[i]f[j]1l)2

= d p [ j ] + f [ j ] 2 − 2 × f [ j ] × ( 2 [ i ] − 1 ) − 2 × l × f [ j ] = dp[j]+f[j]^2-2\times f[j]\times (2[i]-1) - 2\times l \times f[j] =dp[j]+f[j]22×f[j]×(2[i]1)2×l×f[j]

当存在一个 j > k 且 j 比 k 优 秀 的 条 件 是 j>k且j比k优秀的条件是 j>kjk
d p [ j ] + ( f [ i ] − f [ j ] − 1 − l ) 2 &lt; d p [ k ] + ( f [ i ] − f [ k ] − 1 − l ) 2 dp[j]+(f[i]-f[j]-1-l)^2 &lt; dp[k]+(f[i]-f[k]-1-l)^2 dp[j]+(f[i]f[j]1l)2<dp[k]+(f[i]f[k]1l)2

经过一波化简
d p [ j ] + f [ j ] 2 − d p [ k ] − f [ k ] 2 f [ j ] − f [ k ] &lt; 2 × ( f [ i ] − l ) \frac{dp[j]+f[j]^2-dp[k]-f[k]^2}{f[j]-f[k]} &lt; 2\times (f[i]-l) f[j]f[k]dp[j]+f[j]2dp[k]f[k]2<2×(f[i]l)

然后直接套上斜率优化即可

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
#define int long long
using namespace std;
inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
const int maxn = 4e5+1e2;
struct Point
{
	int x,y,num;
}; 
Point q[maxn];
int n,m;
int sum[maxn];
int val[maxn];
int f[maxn];
int head=1,tail=0;
int dp[maxn];
int chacheng(Point x,Point y)
{
	return x.x*y.y-x.y*y.x;
}
bool count(Point i,Point j,Point k)
{
	Point x,y;
	x.x=k.x-i.x;
	x.y=k.y-i.y;
	y.x=k.x-j.x;
	y.y=k.y-j.y;
	if (chacheng(x,y)<=0) return true;
	return false;
}
void push(Point x)
{
	while (tail>=head+1 && count(q[tail-1],q[tail],x)) tail--;
	q[++tail]=x;
}
void pop(int lim)
{
   while (tail>=head+1 && q[head+1].y-q[head].y<lim*(q[head+1].x-q[head].x)) head++; 
} 
signed main()
{
  n=read();
  int l=read();
  for (int i=1;i<=n;i++) val[i]=read();
  for (int i=1;i<=n;i++) sum[i]=sum[i-1]+val[i];
  for (int i=1;i<=n;i++) f[i]=i+sum[i];
  push((Point){0,0,0});
  for (int i=1;i<=n;i++)
  {
  	 pop(2*(f[i]-l));
  	 int now = q[head].num;
  	 dp[i]=dp[now]+(f[i]-f[now]-1-l)*(f[i]-f[now]-1-l);
  	 push(Point{f[i],dp[i]+(f[i]+1)*(f[i]+1),i});
  	// cout<<i<<" "<<dp[i]<<endl;
  }
  cout<<dp[n]<<endl;
  return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]和引用\[2\]的描述,题目中的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状数组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值