见面会EX - dp - 斜率优化 - 单调队列

题目大意:参考这篇blog,但是数据范围是1e7。
题解:这题居然有线性做法是真的秀……
就是这个题不能直接线性的原因是斜率优化没办法支持删除信息,因此需要用分治/线段树等来去掉删除。
然后有一个黑科技:
考虑将序列划分为若干段,使得不存在一个转移区间同时和至少三个段有交。
划分方法是,由于转移区间端点是不降的,因此就是从左端点开始能向右就向右,可以发现这样划分是正确的。
这样有什么好处呢?会发现一个转移区间是由一段的后缀和一段的前缀拼起来的!
然后就可以分开转移,每一种情况都可以线性。然后这个题就线性了。

#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 debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
namespace INPUT_SPACE{
	const int BS=(1<<24)+5;char Buffer[BS],*HD,*TL;inline int gc() { if(HD==TL) TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);return (HD==TL)?EOF:*HD++; }
	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; }
}using INPUT_SPACE::inn;
const int N=300010;const lint inf=LLONG_MIN/100;const db eps=1e-9;
int vl[N],vr[N],L[N];
namespace GETL{
	int sl[N],sr[N];
	inline int getL(int n)
	{
		int hl=1,hr=1,tl=0,tr=0,j=1;
		rep(i,1,n)
		{
			while(tl>=hl&&vl[i]>=vl[sl[tl]]) tl--;sl[++tl]=i;
			while(tr>=hr&&vr[i]<=vr[sr[tr]]) tr--;sr[++tr]=i;
			while(vl[sl[hl]]>vr[sr[hr]]) hl+=(sl[hl]==j),hr+=(sr[hr]==j),j++;
			L[i]=j-1;
		}
		return 0;
	}
}
inline int dcmp(db x) { return (x<-eps)?-1:(x>eps); }
struct Line{
	lint k,b;Line(lint _k=0,lint _b=inf) { k=_k,b=_b; }
	inline lint operator()(lint x)const { return k*x+b; }
	inline db getis(const Line &L)const { return -((db)b-L.b)/(k-L.k); }
};
struct Queue{
	int t;Line f[N];inline int clear() { return t=0; }
	inline int add(Line F) { while(t>1&&dcmp(F.getis(f[t])-f[t].getis(f[t-1]))>=0) t--;f[++t]=F;return 0; }
	inline lint query(lint x) { if(!t) return inf;while(t>1&&f[t](x)<=f[t-1](x)) t--;return f[t](x); }
}q;int l[N],r[N];lint f[N];
int main()
{
	int n=inn(),bc=0;rep(i,1,n) vl[i]=inn(),vr[i]=inn()-1;GETL::getL(n);
	for(int i=0,j=0;i<n;i=++j) { while(j+1<n&&L[j+2]<=i) j++;l[++bc]=i,r[bc]=j; }
	f[0]=0;rep(i,1,n) f[i]=inf;
	rep(i,1,bc)
	{
		q.clear();int k=l[i];
		for(int j=r[i]+1;j>l[i];j--)
		{
			while(k>L[j]) k--,q.add(Line(k,f[k]+k*(k+1ll)/2));
			f[j]=max(f[j],q.query(-j)+j*(j-1ll)/2);
		}
		q.clear();
		rep(j,l[i]+1,r[i]+1) q.add(Line(-(j-1),f[j-1]+(j-1ll)*j/2)),f[j]=max(f[j],q.query(j)+j*(j-1ll)/2);
	}
	return !printf("%lld\n",f[n]);
}

猜你喜欢

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