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\)
那么根据之前的分析有
即枚举上一次割断前 \(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]);
}