P7480 Reboot from Blue(cdq 分治优化 dp)
发现题解里面没有纯\(cdq\)做法,这里提供一下
首先把所有加油站按\(c\)排序,容易得到
\[f[i] = min(f[j] + c[i] * ∣x[i]-x[j]|)
\]
表示从\(j\rightarrow i\)的一次转移
f[i]表示\(i\rightarrow End\)的最小花费
显然复杂度是\(O(n^2)\)的,考虑优化
我开这个题的时候最先想到的是贪心算法 , 后来觉得贪心可能会处理挺多细节,就开始换成\(dp\)思考
显然对贪心来说,如果i要转移的话,一定是向左右两边油价更低的地方转移,然后步步逼近\(End\)。
我们将这个贪心思想用到\(dp\)优化里面,对上面的方程来说要求\(c\)有序,然后我们现在还需要找与当前点最接近的\(x\)
于是我们优化\(dp\)的目标就已经转化为了如何实现这个二维偏序。
接下来的就很好说了吧,\(cdq\)维护二维偏序、\(set+lower\)_\(boun\)d维护凸包、李超线段树维护一次函数(你随便挑
这里用的是\(cdq\)维护二维偏序
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define INF 1ll<<30
template<typename _T>
inline void read(_T &x)
{
x=0;char s=getchar();int f=1;
while(s<'0'||s>'9') {f=1;if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
x*=f;
}
const int p=1e5+5;
struct gas{
int c,x,id;
friend bool operator<(gas a,gas b)
{
return a.c < b.c;
}
}pos[p];
int f[p];
inline int dis(int i,int j)
{
return abs(pos[i].x - pos[j].x);
}
inline bool cmp(gas A,gas B)
{
return A.x < B.x;
}
inline void cdq(int l,int r)
{
if(l == r) return ;
int mid = l+r>>1;
cdq(l,mid);
sort(pos+l,pos+mid+1,cmp);
for(int i=mid+1;i<=r;i++)
{
int pl = upper_bound(pos + l ,pos+mid+1,pos[i],cmp) - pos;
if(pl == mid + 1)
{
pl -- ;
f[pos[i].id] = min(f[pos[pl].id] + abs(pos[pl].x - pos[i].x) *pos[i].c , f[pos[i].id]);
}
else
{
f[pos[i].id] = min(f[pos[pl].id] + abs(pos[pl].x - pos[i].x) *pos[i].c , f[pos[i].id]);
}
if(pl != 1)pl--;
f[pos[i].id] = min(f[pos[pl].id] + abs(pos[pl].x - pos[i].x) *pos[i].c , f[pos[i].id]);
}
sort(pos+l,pos+mid+1);
cdq(mid+1,r);
return;
}
signed main()
{
int n,s,t;
read(n);
read(s);
read(t);
for(int i=1,c,x;i<=n;i++)
{
read(c);
read(x);
pos[i].c = c;
pos[i].x = x;
pos[i].id = i;
}
pos[n+1].c = 0;
pos[n+1].x = t;
pos[n+1].id = n+1;
memset(f,0x3f,sizeof(f));
f[n+1] = 0;
sort(pos+1,pos+2+n);
cdq(1,n + 1);
for(int i=1;i<=n + 1;i++)
{
if(pos[i].x == s)
{
cout<<f[pos[i].id];
return 0;
}
}
}