专栏文章

题解:P3628 [APIO2010] 特别行动队

P3628题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mip5htgb
此快照首次捕获于
2025/12/03 06:29
3 个月前
此快照最后确认于
2025/12/03 06:29
3 个月前
查看原文
看见题解区没有用李超树的,我当然要来水水咕值了。
套路的,我们先写暴力的 dp 式子。
dpi=minj=0i1{dpj+a×(sumisumj)2+b×(sumisumj)+c}dp_i=\displaystyle \min _{j=0}^{i-1}\{dp_j+a\times (sum_i-sum_j)^2+b \times (sum_i-sum_j)+c\}
然后,让我们把它完全展开以方便优化。
dpi=dpj+a×sumi2+a×sumj22a×sumi×sumj+b×sumib×sumj+cdp_i=dp_j+a\times sum_i^2+a \times sum_j^2-2a\times sum_i\times sum_j+b\times sum_i-b\times sum_j+c
我们发现其中只有一项同时和 iijj 相关,很明显的斜率优化。我们把它美化一下。
dpi=2a×sumj×sumi+dpj+a×sumj2b×sumj+a×sumi2+b×sumi+cdp_i=-2a\times sum_j\times sum_i+dp_j+a \times sum_j^2-b\times sum_j+a\times sum_i^2+b\times sum_i+c
这个时候斜率优化的式子就很明显了,令 y=dpi,k=2a×sumj,x=sumi,b=a×sumj2b×sumjy=dp_i,k=-2a\times sum_j,x=sum_i,b=a \times sum_j^2-b\times sum_j,那它就是函数取最值的板子,可以使用李超树维护。
还有一点小细节就是李超树不能动态开点,否则会炸空间,需要将 xx 的取值离散化之后再插到李超树里面。
下面给出我的实现并不精细的代码。
CPP
// Problem: P3628 [APIO2010] 特别行动队
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3628
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Author: Binah_cyc

#include<bits/stdc++.h>
using namespace std;
#define int long long
constexpr int N=1e6+5;
int n,A,B,C,a[N];
vector<int> b;

struct Segment
{
	int k,b;
	int operator()(int x){return k*::b[x]+b;}
	Segment(int _k=0,int _b=INT_MIN){k=_k,b=_b;}
}t[N<<2];
void change(Segment id,int tl=1,int tr=n,int p=1)
{
	int mid=(tl+tr)>>1;
	if(t[p](mid)<id(mid)) swap(t[p],id);
	if(t[p](tl) <id(tl) ) change(id,tl,mid,p<<1);
	if(t[p](tr) <id(tr) ) change(id,mid+1,tr,p<<1|1);
}
int ask(int x,int tl=1,int tr=n,int p=1)
{
	if(tl==tr) return t[p](x);
	int mid=(tl+tr)>>1;
	if(x<=mid) return max(t[p](x),ask(x,tl,mid,p<<1));
	else return max(t[p](x),ask(x,mid+1,tr,p<<1|1));
}
//以上是李超树板子,我实现的可能有点丑

int dp[N];

main()
{
	cin.tie(0)->sync_with_stdio(false);
	cin>>n>>A>>B>>C;
	for(int i=1;i<=n;i++) cin>>a[i],a[i]+=a[i-1];
	b.push_back(-1);
	for(int i=1;i<=n;i++) b.push_back(a[i]);
	sort(b.begin(),b.end()),b.erase(unique(b.begin(),b.end()),b.end());
	change({0,0});//它代表dp[0]
	for(int i=1;i<=n;i++)
	{
		dp[i]=ask(lower_bound(b.begin(),b.end(),a[i])-b.begin())+A*a[i]*a[i]+B*a[i]+C;//转移
		change({-2*A*a[i],dp[i]+A*a[i]*a[i]-B*a[i]});//插线段
	}
	cout<<dp[n];
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...