题解:P17741 [ICPC 2019 Xuzhou R] K-rectangle
卡帕瓦_KAPawa · · 题解
小朋友们你们好啊,我比较喜欢小常数多哥哥算法,所以我使用 CDQ 分治+李超线段树 过了这道题。
首先容易发现矩形之间交叉一定不优,所以我们不难写出一个非常暴力的
也就是,枚举最后一个矩形覆盖了多少个点,直接进行转移。
接下来考虑优化。发现这个转移式子的形式非常像一个斜率优化,但是这里有个区间
假设我们在
我们定义
分类讨论
我们希望调用 CDQ(l,r) 来算出
仿照上面的做法,我们定义
此时,
我们要分情况讨论转移,即分当
当
因为
此时就是个斜率优化模板了。我喜欢轮椅数据结构,所以我使用了李超树。那么这个转移式的直线斜率就是
当
因为
那么这个转移式的直线斜率就是
然后就做完了。时间复杂度为
:::success[代码]
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=8e5+10,inf=5e17+10,H=1e6+10,V=2e6+20;
namespace IO{
const static int _M=1e6+5,_K=2e6+5;
char ibuf[_K],obuf[_K],_st[_K],*p1(0),*p2(0);
static int _cnt=0,_top=0;
inline void flush(){return fwrite(obuf,1,_cnt,stdout),_cnt=0,void();}
struct AutoFlush{~AutoFlush(){flush();}}flusher;
inline char gc(){return (p1==p2&&(p2=(p1=ibuf)+fread(ibuf,1,_K,stdin),p1==p2)?EOF:*p1++);}
template<typename T>inline void read(T&x){
x=0;char c;bool f=0;
do{c=gc();if(c=='-') f=1;}while(c<48);do{x=(x<<3)+(x<<1)+(c^48);c=gc();}while(c>=48);
return(f?x=~x+1,void():void()),void();
}
template<typename T>inline void readmod(T&x,ll mod){
x=0;char c;
do{c=gc();}while(c<48);do{x=((x<<3)+(x<<1)+(c^48))%mod;c=gc();}while(c>=48);
return;
}
inline void pc(char c){obuf[_cnt++]=c;if(_cnt>=_M)flush();return;}
inline void ps(string s){for(auto c:s)obuf[_cnt++]=c;if(_cnt>=_M)flush();return;}
template<typename T>inline void write(T x,char c='\0'){
if(x<0) pc('-'),x=-x;
do{_st[++_top]=(x%10)|48;}while(x/=10);while(_top)pc(_st[_top--]);
return(c?pc(c):void()),void();
}
}using namespace IO;
ll n,k,x[N],y[N],a[N],b[N],dp[N];
struct sgt{
ll cnt=0,tot=0,root=0,now=0;
struct Line{ll k,b;}L[N<<2]={0,inf};
struct node{ll ls,rs,num,tag;}tr[N<<2];
#define ls tr[p].ls
#define rs tr[p].rs
#define num tr[p].num
#define tag tr[p].tag
inline void clear(){now++,tot=0;return;}
inline ll calc(ll id,ll x){return L[id].k*(x-H)+L[id].b;}
inline void upd(ll &p,ll l,ll r,ll u){
if(!p) p=++cnt;
if(tag!=now) return tag=now,num=u,void();
ll mid=(l+r)>>1;
ll op1=calc(u,mid),op2=calc(num,mid);
if(op1<op2) swap(u,num);
if(l==r) return;
op1=calc(u,l),op2=calc(num,l);
if(op1<op2) upd(ls,l,mid,u);
op1=calc(u,r),op2=calc(num,r);
if(op1<op2) upd(rs,mid+1,r,u);
return;
}
inline void add(ll k,ll b){L[++tot]={k,b};upd(root,1,V,tot);}
inline ll query(ll x,ll p,ll l,ll r){
if(!p||tag!=now) return inf;
ll mid=(l+r)>>1,ans=calc(num,x);
if(l==r) return ans;
else if(mid>=x) return min(ans,query(x,ls,l,mid));
else return min(ans,query(x,rs,mid+1,r));
}
}T;
inline void CDQ(ll l,ll r){
if(l==r) return;
ll mid=(l+r)>>1;
CDQ(l,mid);
a[mid]=0;
for(int i=mid-1;i>=l;i--) a[i]=max(y[i+1],a[i+1]);
b[mid]=0;
for(int i=mid+1;i<=r;i++) b[i]=max(y[i],b[i-1]);
T.clear();
ll j=l;
for(int i=r;i>mid;i--){
while(j<=mid&&a[j]>=b[i]) T.add(a[j],dp[j]+(k-x[j+1])*a[j]),j++;
dp[i]=min(dp[i],T.query(x[i]+H,T.root,1,V));
}
T.clear();
j=mid;
for(int i=mid+1;i<=r;i++){
while(j>=l&&a[j]<=b[i]) T.add(x[j+1],dp[j]),j--;
dp[i]=min(dp[i],T.query(H-b[i],T.root,1,V)+b[i]*(x[i]+k));
}
return CDQ(mid+1,r);
}
int main(){
read(n),read(k);
for(int i=1;i<=n;i++) read(x[i]),read(y[i]),dp[i]=inf;
CDQ(0,n);
write(dp[n],'\n');
return 0;
}
:::