[集训队作业2018]UOJ 430 line - dp - 线段树 - 斜率优化 - CDQ分治

发现以前学习的斜率优化全是假的。
学习到了新的斜率优化,比原先的那个更方便一些。
形如 Y i = Y i + min j [ 1 , i ) { K j X i + B j } Y_i=Y'_i+\min_{j\in[1,i)}\{K_jX_i+B_j\} 的dp,其中 Y Y 是要计算的dp, Y , X Y',X 都是已知量, K , B K,B 是算出 Y Y 后可以得知的量。
那么直接将 F j ( x ) = K j x + B j F_j(x)=K_jx+B_j 视为一个关于 x x 的变量,那么可以直接维护动态半平面交一样的东西;一种方法是和维护动态凸包类似,维护个平衡树,每次二分即可;或者可以直接李超线段树;或者可以套个分治然后静态的半 半平面交(所有半平面方向一致所以比朴素半平面交好写);如果是区间询问的话可以类似SDOI向量集套一个线段树维护半平面交;如果询问点单调或者加入直线斜率单调都可以套单调数据结构。
这种方法可以一眼看出函数的斜率和截距,以及要维护的是上还是下凸包。

回到正文:
题目大意:有一个方程形如 f i = min j = l i i 1 { ( max ( t j + 1 , , t i ) ) s u f i + 1 + f j } f_i=\min_{j=l_i}^{i-1}\{(\max(t_{j+1},\dots,t_i))suf_{i+1}+f_j\} ,其中 t t 是非负整数, s u f suf 是单调不增的数列。
那么首先考虑那个看起来像是斜率的max,可以用一个CDQ分治去处理。
假设分治区间[L,R],先求完左半边[L,mid],然后考虑[L,mid]对(mid,R]的影响。
考虑 i ( m i d , R ] i\in(mid,R] [ max ( l i , L ) , m i d ] [\max(l_i,L),mid] 可以看做两个部分:找到一个 p p ,使得 max k = p + 1 m i d + 1 t k m x , ( p = l 1   o r   t p > m x ) \max_{k=p+1}^{mid+1}t_k\le mx,(p=l-1\ or\ t_p>mx) ,其中 m x = max j = m i d i t j mx=\max_{j=mid}^i t_j 。那么这部分转移到 f i f_i ,那个转移中的max都会是 m x mx ,那么在这个区间 [ p , m i d ] [p,mid] 中找一个最小的 f f 即可;否则这个转移中的max就会和 i i 没有关系,可以预先用线段树维护好半平面交。注意到直线的斜率是不降的,而询问的值又是不升的,所以两部分都可以线性,总复杂度 O ( n lg 2 n ) O(n\lg^2n)
(貌似有 O ( n l g n ) O(nlgn) 神仙做法但是并不会XD)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn()
{
	int x,ch;while((ch=gc)<'0'||ch>'9');
	x=ch^'0';while((ch=gc)>='0'&&ch<='9')
		x=(x<<1)+(x<<3)+(ch^'0');return x;
}
const int N=100010<<2;
lint val[N],f[N],suf[N],mnf[N];int t[N],w[N],l[N];
struct Line{
	lint k,b;Line(lint _k=0,lint _b=0) { k=_k,b=_b; }
	inline db is(const Line &L)const { return ((db)L.b-b)/((db)k-L.k); }
	inline lint F(lint x)const { return k*x+b; }
	inline ostream& show()const { return debug(k)sp,debug(b); }
};
namespace Convex_space{
	lint k[N],b[N];
	struct Convex{
		vector<Line> L;int c;
		inline int build(lint *_k,lint *_b,int n)
		{
			L.resize(n);int t=0,ns=n;n=0;
			for(int i=0,j=0;i<ns;k[n++]=_k[i],i=++j)
				for(b[n]=_b[i];j+1<ns&&_k[j+1]==_k[i];j++) b[n]=min(b[n],_b[j+1]);
			for(int i=0;i<n;L[t++]=Line(k[i],b[i]),i++)
				while(t>1&&L[t-2].is(L[t-1])>=L[t-1].is(Line(k[i],b[i]))) t--;
			return L.resize(t),c=t-1,0;
		}
		inline lint query(lint x) { while(c&&L[c-1].is(L[c])>=x) c--;return L[c].F(x); }
	}convex[N];
}using Convex_space::convex;
inline int build(int x,int l,int r)
{
	convex[x].build(val+l,f+l,r-l+1);
	int mid=(l+r)>>1;if(l==r) return 0;
	build(x<<1,l,mid),build(x<<1|1,mid+1,r);
	return 0;
}
inline lint query(int x,int l,int r,int s,int t,lint p)
{
	if(s>t) return LLONG_MAX;
	if(s<=l&&r<=t) return convex[x].query(p);
	int mid=(l+r)>>1;lint ans=LLONG_MAX;
	if(s<=mid) ans=min(ans,query(x<<1,l,mid,s,t,p));
	if(mid<t) ans=min(ans,query(x<<1|1,mid+1,r,s,t,p));
	return ans;
}
inline int solve(int L,int R)
{
	if(L==R) return 0;int mid=(L+R)>>1;
	solve(L,mid),val[mid]=t[mid+1],mnf[mid]=f[mid];
	for(int i=mid-1;i>=L;i--)
		val[i]=max(val[i+1],(lint)t[i+1]),mnf[i]=min(mnf[i+1],f[i]);
	build(1,L,mid);
	for(int i=mid+1,mx=0,ps=mid+1;i<=R;i++)
	{
		mx=max(mx,t[i]);while(ps>L&&val[ps-1]<=mx) ps--;if(l[i]>mid) continue;
		f[i]=min(f[i],mnf[max(ps,l[i])]+mx*suf[i+1]);
		f[i]=min(f[i],query(1,L,mid,max(l[i],L),ps-1,suf[i+1]));
	}
	return solve(mid+1,R);
}
int main()
{
	int n=inn();rep(i,1,n) l[i]=inn(),t[i]=inn(),w[i]=inn();
	for(int i=n;i;i--) suf[i]=suf[i+1]+w[i];rep(i,1,n) f[i]=LLONG_MAX;
	return solve(0,n),!printf("%lld\n",f[n]);
}

猜你喜欢

转载自blog.csdn.net/Mys_C_K/article/details/87950413