luogu P6047 丝之割
题面传送门
显然对于两个二元组\((x_i,y_i)\)与\((x_j,y_j)\),如果\(x_i\leq x_j\)且\(y_i>y_j\)那么第二个二元组实际上对于答案是没有贡献的。
所以处理一遍得到的东西满足两个均单调上升。
设\(minx_i=\min\limits_{j=1}^{i}{x_i}\),\(miny_i=\min\limits_{j=i}^{n}{y_i}\)那么就有显然\(O(n^2)\)的\(dp\)式了:设\(dp_i\)表示割掉前\(i\)根所需的最小费用,则有式子\(dp_i=\min\limits_{j=0}^{i-1}{dp_j+minx_{x_{j+1}-1}*miny_{y_i+1}}\)
然而过不去。看上去这个东西可以斜优。
设\(k<j\)且\(k\)劣于\(j\)
则\(dp_j+minx_{x_{j+1}-1}*miny_{y_i+1}<dp_k+minx_{x_{k+1}-1}*miny_{y_i+1}\)
化简得\(\frac{dp_j-dp_k}{minx_{x_{k+1}-1}-minx_{x_{j+1}-1}}<miny_{y_i+1}\)
单调队列维护一下即可\(O(n)\)
代码实现:
#include<cstdio>
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define Y(a) (dp[a])
#define X(a) (minx[s[a+1].x-1])
using namespace std;
int n,m,k,x,y,z,h,f[300039],q[300039],head,tail,minx[300039],miny[300039],maxn;
long long dp[300039];
struct yyy{int x,y;}s[300039],tmp;
inline double slope(int x,int y){return X(x)==X(y)?1e9:(Y(y)-Y(x))*1.0/(X(x)-X(y));}
int main(){
//freopen("1.in","r",stdin);
register int i,j;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++) scanf("%d",&minx[i]);
for(i=1;i<=n;i++) scanf("%d",&miny[i]);
for(i=2;i<=n;i++) minx[i]=min(minx[i],minx[i-1]);
for(i=n-1;i;i--) miny[i]=min(miny[i],miny[i+1]);
for(i=1;i<=m;i++) scanf("%d%d",&x,&y),f[x]=max(f[x],y);
for(i=1;i<=n;i++) if(f[i]>maxn) maxn=f[i],s[++h]=(yyy){i,maxn};
for(i=1;i<=h;i++){
tmp=s[i];
while(head<tail&&slope(q[head],q[head+1])<miny[tmp.y+1]) head++;
j=q[head];dp[i]=dp[j]+(long long)minx[s[j+1].x-1]*miny[s[i].y+1];
while(head<tail&&slope(q[tail-1],q[tail])>slope(q[tail],i)) tail--;
q[++tail]=i;
}
printf("%lld\n",dp[h]);
}