斜率优化 dp
P6047 丝之割
题目大意
给出 \(m\) 个二元组 \((u_i,v_i)\),现在可以选定 \(i,j\in [1,n]\) 然后花费 \(a_i\times b_j\) 的代价将所有满足 \(u>i,v<j\) 的二元组破坏掉,问破坏所有二元组的最小代价,保证 \(n,m\leq 3\times 10^5,u\in(1,n],v\in [1,n)\)。
Solution
首先注意到一个性质:
如果存在两个交叉的二元组,即 \(u_i\leq u_j,v_i\geq v_j\),那么当我们破坏 \((u_i,v_i)\)时,\((u_j,v_j)\) 也会被一起破坏,因此可以直接去掉所有交叉的二元组。
现在开始 \(dp\),我们先将所有二元组按 \(u\) 排序,此时 \(v\) 也是,设 \(dp_i\) 表示前 \(i\) 个二元组都被破坏的最小代价,那么就容易想到记录 \(a\) 的前缀 \(min\),和 \(b\) 的后缀 \(min\) 转移方程:
\[dp_i=\min_{\\j<i}\{ dp_j +mina_{u_j -1}\times minb_{v_i+1} \}
\]
发现可以斜率优化,转换一下:
\[dp_i=dp_j +mina_{u_j -1}\times minb_{v_i+1}
\]
\[dp_j=-mina_{u_j -1}\times minb_{v_i+1}+dp_i
\]
就相当于多个点 \((-mina_{u_j-1},dp_j)\),然后用斜率为 \(minb_{v_i+1}\) 的直线截取。
观察到 \(-mina_{u_j-1}\) 随 \(j\) 增大而增大,因此直接用单调栈维护下凸包。
且 \(minb_{v_i+1}\) 随 \(i\) 增大而增大,因此可以直接用双指针维护。
Code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
int main()
{
int n,m;
scanf("%d%d",&n,&m);
vector<long long>a(n+1);
vector<long long>b(n+1);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
for(int i=1;i<=n;i++)
{
scanf("%lld",&b[i]);
}
vector<pair<int,int>>seg(m+1);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&seg[i].first,&seg[i].second);
}
sort(seg.begin()+1,seg.end());
vector<int>u(m+1,0);
vector<int>v(m+1,0);
int tot=1;
for(int i=1;i<=m;i++)
{
if(seg[i].second<=v[tot-1])
{
continue;
}
u[tot]=seg[i].first;
v[tot]=seg[i].second;
tot++;
}
vector<long long>min_a(n+2,0);
vector<long long>min_b(n+2,0);
min_a[0]=1e18;
min_b[n+1]=1e18;
for(int i=1;i<=n;i++)
{
min_a[i]=min(min_a[i-1],a[i]);
}
for(int i=n;i>=1;i--)
{
min_b[i]=min(min_b[i+1],b[i]);
}
vector<long long>dp(tot+1,1e18);
dp[0]=0;
vector<pair<long long,long long>>stk(tot+5);
int s=1,t=1;
stk[1]=make_pair(-min_a[u[1]-1],0);
auto slope=[](pair<long long,long long>a,pair<long long,long long>b)
{
if(a.first==b.first)
{
return 1e18l;
}
return (long double)(b.second-a.second)/(long double)(b.first-a.first);
};
for(int i=1;i<tot;i++)
{
while(s<t&&(long double)(min_b[v[i]+1])>slope(stk[s],stk[s+1]))
{
s++;
}
dp[i]=stk[s].second-stk[s].first*min_b[v[i]+1];
if(i!=tot-1)
{
while(s<t&&slope(stk[t-1],stk[t])>slope(stk[t],make_pair(-min_a[u[i+1]-1],dp[i])))
{
t--;
}
stk[++t]=make_pair(-min_a[u[i+1]-1],dp[i]);
}
}
cout<<dp[tot-1]<<endl;
return 0;
}

浙公网安备 33010602011771号