Loading

P6047 丝之割

P6047 丝之割

挺有意思的一道题呢!

首先我们发现,如果我们要割掉弦 \(i\) ,那么所花的代价最小是 \(suf_{u_i+1}\times pre_{v_i-1}\)

其中 \(suf_i\) 表示 数组a 的后缀最小值,\(pre_i\) 表示 数组b 的前缀最小值。

这个很显然,第一行从后找一个最小的,第二行从前找一个最小的,连起来这根弦就割掉了。

尝试搞个dp出来,如果按照 \(u\) 排序割掉一段弦 \(i,\cdots,j\) 呢?最靠左 \(v\) 不知道,如果拿DS维护 \(v\) 的区间最值那就很难继续优化了,这个dp写不下去。

换个方向。

思考一会可以隐约感觉到有些弦是没用的,就是割掉另一根弦一定会割掉它,那么这根弦就是没用的了。

考虑有两根弦 \(i,j\) ,割掉 \(i\) 必然割掉 \(j\) 所需满足的条件,显然是 \(u_i<u_j\)\(v_i>v_j\)

这东西不就是两根弦 \(i,j\) 有交的充要条件吗!

那么我们可以通过排序来得出所有“有用”的弦,这些有用的弦一定是没有交点的

形象一点放个图 谁能教我在ubuntu上怎么画直线啊

根据这个性质就可以dp了

\(dp_i\) 表示割断前 \(i\) 根弦的最小代价,记有用的弦为 \(q\)

那么根据之前的分析有

\[dp[0]=0\\ dp[i]=\min\{dp[j]+pre[u[q[j+1]]-1]\times suf[v[q[i]]+1] \} \]

即枚举上一次割断前 \(j\) 根弦,这次割断第 \(j+1\)\(i\)根弦

这个东西可以看作是:有 \(i\) 根直线,每一根的斜率是 \(pre[u[q[j+1]]-1]\)\(y\) 轴截距是 \(dp[j]\)\(dp[i]\)\(x=suf[v[q[i]]+1]\) 时所有直线的最小值

直接上李超树即可

唯一的细节大概就是排序了,按照 \(u\) 为第一关键字从小到大,\(v\) 为第二关键字从大到小排,从前往后扫到与之前直线不相交就加入 \(q\)

为啥题解里有 \(O(n)\) 的但是我这个 \(O(n\log n)\) 的反而是最优解啊

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef double db;
#define mkp(x,y) make_pair(x,y)
#define fi first
#define se second
#define pb(x) push_back(x)
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
	return f?x:-x;
}
#define N 300005
#define M 1000005
#define inf 1000000000000000000ll
#define T (M<<2)
int n,m,suf[N],pre[N],a[N],b[N],cnt,MX;
LL dp[N];
struct node{
	int u,v;
	inline bool operator < (const node&t)const{return u!=t.u?u<t.u:v>t.v;}
}p[N],q[N];
pair<int,LL>li[N];
int v[T];
#define lc (p<<1)
#define rc (p<<1|1)
#define val(x,y) (1ll*li[x].fi*y+li[x].se)
void update(int id,int l=1,int r=MX,int p=1){
	if(!v[p])return v[p]=id,void();
	LL al=val(id,l),ar=val(id,r),bl=val(v[p],l),br=val(v[p],r);
	if(al>=bl&&ar>=br)return;
	if(al<=bl&&ar<=br)return v[p]=id,void();
	int mid=(l+r)>>1;
	db inter=1.*(li[id].se-li[v[p]].se)/(li[v[p]].fi-li[id].fi);
	if(al<=bl){
		if(inter<=mid)update(id,l,mid,lc);
		else update(v[p],mid+1,r,rc),v[p]=id;
	}else{
		if(inter>mid)update(id,mid+1,r,rc);
		else update(v[p],l,mid,lc),v[p]=id;
	}
}
LL query(int X,int l=1,int r=MX,int p=1){
	LL res=inf;
	if(v[p])res=val(v[p],X);
	if(l==r)return res;
	int mid=(l+r)>>1;
	if(X<=mid)res=min(res,query(X,l,mid,lc));
	else res=min(res,query(X,mid+1,r,rc));
	return res;
}
signed main(){
	n=read(),m=read();
	for(int i=1;i<=n;++i)MX=max(MX,a[i]=read());
	for(int i=1;i<=n;++i)MX=max(MX,b[i]=read());
	for(int i=1;i<=m;++i)p[i].u=read(),p[i].v=read();
	pre[1]=a[1];for(int i=2;i<=n;++i)pre[i]=min(pre[i-1],a[i]);
	suf[n]=b[n];for(int i=n-1;i>=1;--i)suf[i]=min(suf[i+1],b[i]);
	sort(p+1,p+m+1);
	int mm=0;
	for(int i=1;i<=m;++i)if(p[i].u>q[mm].u&&p[i].v>q[mm].v)q[++mm]=p[i];
	m=mm;
	for(int i=0;i<=m;++i){
		if(!i)dp[i]=0;
		else dp[i]=query(suf[q[i].v+1]);
		li[++cnt]=mkp(pre[q[i+1].u-1],dp[i]);
		update(cnt);
		// for(int j=0;j<i;++j)dp[i]=min(dp[i],dp[j]+1ll*pre[q[j+1].u-1]*suf[q[i].v+1]);
	}
	printf("%lld\n",dp[m]);
}
posted @ 2020-10-31 11:11  zzctommy  阅读(162)  评论(0编辑  收藏  举报