P6047 丝之割
看到题目的第一眼感觉 \(u,v\) 坐标纵横交叉,看着很不可做。但是细想一下就会发现,如果 \(u_i>u_j\) 并且 \(v_i<v_j\) 的话,我们在消除 \(j\) 的时候一定会同步消除 \(i\),我们把相交的边处理完之后,我们的边集就变成若干条无交的边了。
有了这个性质,我们很容易写出 \(dp\) 方程,我们令 \(dp_i\) 表示消除前 \(i\) 条边所需的最小代价,同时开两个数组 \(pre\) 和 \(suf\) 分别维护前缀最小值,和后缀最小值,就可以得到转移方程:
这样我们就有了一个 \(O(n^2)\) 的做法,观察数据范围,我们可以拿到 79pts 的高分,考虑优化。
典型的斜率优化,很容易推导得到斜率为 \((dp[i]-dp[j])/(pre_{u_{j+1}-1}-pre_{u_{i+1}-1})\),注意判分母为 0 的情况即可。
code:
//79pts
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=3e5+10;
int n,mm,m;
ll a[N],b[N],dp[N],pmi[N],smi[N];//dpi biaoshi xiaomei qian i ge xian suo xu de zui xiao dai jia
struct node{
int u,v;
bool operator <(const node &a)const{
return u==a.u?v<a.v:u<a.u;
}
}x[N],s[N];
ll calc(int l,int r){
return pmi[s[l].u-1]*smi[s[r].v+1];
}
int main(){
scanf("%d%d",&n,&mm);
for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
for(int i=1;i<=n;++i)scanf("%lld",&b[i]);
for(int i=1;i<=mm;++i)scanf("%d%d",&x[i].u,&x[i].v);
sort(x+1,x+mm+1);
for(int i=1;i<=mm;++i)
if(x[i].v>s[m].v)s[++m]=x[i];
for(int i=0;i<=n+1;++i)pmi[i]=smi[i]=0x3f3f3f3f;
for(int i=1;i<=m;++i)dp[i]=0x3f3f3f3f3f3f3f3f;
for(int i=1;i<=n;++i)pmi[i]=min(pmi[i-1],a[i]);
for(int i=n;i>=1;--i)smi[i]=min(smi[i+1],b[i]);
dp[0]=0;
for(int i=1;i<=m;++i){
for(int j=0;j<i;++j){
dp[i]=min(dp[i],dp[j]+calc(j+1,i));
// cout<<j+1<<" "<<i<<" "<<calc(j+1,i)<<endl;
}
}
cout<<dp[m];
}
//100pts
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=3e5+10;
int n,mm,m;
ll a[N],b[N],dp[N],pmi[N],smi[N];//dpi biaoshi xiaomei qian i ge xian suo xu de zui xiao dai jia
struct node {
int u,v;
bool operator <(const node &a)const {
return u==a.u?v>a.v:u<a.u;
}
} x[N],s[N];
double slope(int i,int j){
if(pmi[s[j+1].u-1]==pmi[s[i+1].u-1])return 1e18;
return 1.0*(dp[i]-dp[j])/(pmi[s[j+1].u-1]-pmi[s[i+1].u-1]);
}
int q[N],hd=1,tl=1;
int main() {
scanf("%d%d",&n,&mm);
for(int i=1; i<=n; ++i)scanf("%lld",&a[i]);
for(int i=1; i<=n; ++i)scanf("%lld",&b[i]);
for(int i=1; i<=mm; ++i)scanf("%d%d",&x[i].u,&x[i].v);
sort(x+1,x+mm+1);
for(int i=1; i<=mm; ++i)
if(x[i].v>s[m].v)s[++m]=x[i];
for(int i=0; i<=n+1; ++i)pmi[i]=smi[i]=0x3f3f3f3f;
for(int i=1; i<=m; ++i)dp[i]=0x3f3f3f3f3f3f3f3f;
for(int i=1; i<=n; ++i)pmi[i]=min(pmi[i-1],a[i]);
for(int i=n; i>=1; --i)smi[i]=min(smi[i+1],b[i]);
dp[0]=0;
for(int i=1; i<=m; ++i) {
while(hd<tl&&slope(q[hd],q[hd+1])<smi[s[i].v+1])hd++;
// cout<<hd<<" "<<tl<<endl;
dp[i]=dp[q[hd]]+pmi[s[q[hd]+1].u-1]*smi[s[i].v+1];
while(hd<tl&&slope(q[tl-1],q[tl])>slope(q[tl],i))tl--;
q[++tl]=i;
// cout<<hd<<" "<<tl<<endl;
}
cout<<dp[m];
}
总结:
本题的关键点在于用贪心的思路去优化我们选取的过程,这种思路很常见,也很容易忽略,在分析题目的时候需要优先考虑。
例题:

浙公网安备 33010602011771号