(倍增优化dp)[NOIP2012 提高组] 开车旅行
写在前面
不要让忧郁侵蚀你的灵魂,要勇敢用你所爱的东西武装自己的内心,充盈自己的精神世界。
[NOIP2012 提高组] 开车旅行 题解
题意
给定若干个点与其海拔高度,每两个点之间的距离为这两点的海拔差的绝对值。点\(i\) 可以到达点\(j\) 当且仅当\(i<j\)。两个人\(A\),\(B\) 轮流开车,\(A\) 开到次近点,\(B\) 开到最近点(距离相同时海拔低的点更近),走到无点再走或者如果继续走总路程将大于给定值时结束。求问:
- 给定一个最大路程\(x_0\),求问当以哪个点为起点时$A \(走过的路程与\)B \(走过的路程之比最小(\)B=0$ 时比值视为正无穷)。若有多个符合条件的点,选取海拔最高的那个。
- 给定若干对数据\(s\) 和\(x\),求问当以\(s\) 为起点,最多走的路程为\(x\) 时\(A、B\) 分别走过的路程是多少。
思路
从每个点出发经过的点和路程是唯一确定的,故可以预处理出来。本人比较笨不知道set咋用,所以手搓了一棵平衡树强求了出来。这里要注意前驱的前驱可能比后继近,同理后继的后继可能比前驱近。
接着考虑求解,如果用暴力枚举跳转复杂度将在\(O(n^2)\) 左右。由于所走的每一段相对独立,且可以靠拼凑转移,所以考虑倍增优化。设计dp数组\(f[i][j][k]\),第一维为走的步数\(2^i\) ,第二维为起点,第三维表示谁先走,0代表A先走,1代表B先走。初始化为当\(i=0\) 时走到各自的下一个点。当\(i=1\)时比较特殊,因为相较上一个状态只走了一步,就要从不同的\(k\) 转移。大致模拟一下就是以\(j\) 为起点\(k\)先走共走两步,先从\(j\) 走到\(f[0][j][k]\),再由另外一个人从这里走一步。所以\(f[1][j][k]=f[0][f[0][j][k]][k\text{^1}]\)。其他情况就是\(k\) 先走\(2^{i-1}\) 步,再走\(2^{i-1}\) 步。因为走偶数步就是走了若干轮,所以可以只由本人走的位置转移。由于我们还要记录走过的路程,所以可以同时求出\(dista[i][j][k],distb[i][j][k]\),走\(2^i\)步,起点为\(j\),\(k\) 先走时\(A、B\) 分别走过的路程。转移方程类似于\(f\)。
考虑求解第一个子问题。枚举每一个起点,就用类似于倍增求LCA的方法跳跳跳就可以了。注意当\(i=0\) 时要换人。由于以比值为基准容易产生精度问题,所以就转化为比较\(da*minb?<db*mina\)。
第二个问题也用与第一个问题一样的办法解决。
然后本题就解决了。难点和槽点主要在于预处理上,甚至预处理的代码长达101行(当然也用我手写平衡树的原因)。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+10,hm=20;
int lg[maxn],minn[maxn],secminn[maxn],dm[maxn],dsm[maxn],minni,mina,minb;
int n,m,h[maxn];
struct node{
int ch[2],val,siz,fa;
}t[maxn];
int tot,root;
inline void pushup(int u){
t[u].siz=t[t[u].ch[0]].siz+t[t[u].ch[1]].siz+1;
}
inline void rotate(int x){
int y=t[x].fa,z=t[y].fa,k=t[y].ch[1]==x;
t[x].fa=z,t[z].ch[t[z].ch[1]==y]=x;
t[t[x].ch[k^1]].fa=y,t[y].ch[k]=t[x].ch[k^1];
t[y].fa=x,t[x].ch[k^1]=y;
pushup(y),pushup(x);
}
inline void splay(int x,int king){
while(t[x].fa!=king){
int y=t[x].fa,z=t[y].fa;
if(z!=king) rotate(((t[y].ch[1]==x)^(t[z].ch[1]==y))?x:y);
rotate(x);
}
if(!king) root=x;
}
inline void find(int x){
int u=root;
while(t[u].val!=x&&t[u].ch[x>t[u].val]) u=t[u].ch[x>t[u].val];
splay(u,0);
}
inline int next(int x,int f){
find(x);
int u=root;
if(t[u].val>x&&f) return u;
if(t[u].val<x&&!f) return u;
u=t[u].ch[f];
while(t[u].ch[f^1]) u=t[u].ch[f^1];
return u;
}
inline void insert(int x,int pos){
int u=root,fa=0;
while(u) fa=u,u=t[u].ch[x>t[u].val];
if(fa) t[fa].ch[x>t[fa].val]=pos;
t[pos].fa=fa;
t[pos].ch[0]=t[pos].ch[1]=0;
t[pos].val=x;
splay(pos,0);
}
int f[hm][maxn][2],dista[hm][maxn][2],distb[hm][maxn][2];
void pre(){
for(int i=n;i>=1;i--){
if(i==n){
insert(h[i],i);
secminn[i]=minn[i]=0;
dm[i]=dsm[i]=1e14;
continue;
}
int pre=next(h[i],0),nxt=next(h[i],1),prepre=next(h[pre],0),nxtnxt=next(h[nxt],1);
if(pre&&nxt){
if(abs(h[i]-h[prepre])<=abs(h[nxt]-h[i])) swap(prepre,nxt);
if(abs(h[i]-h[pre])>abs(h[nxtnxt]-h[i])) swap(pre,nxtnxt);
if(abs(h[i]-t[pre].val)>abs(t[nxt].val-h[i])) swap(pre,nxt);
minn[i]=pre;
secminn[i]=nxt;
}
else if(pre&&!nxt){
minn[i]=pre;
secminn[i]=prepre;
}
else if(nxt&&!pre){
minn[i]=nxt;
secminn[i]=nxtnxt;
}
dm[i]=abs(h[i]-h[minn[i]]);
dsm[i]=secminn[i]!=0?abs(h[i]-h[secminn[i]]):1e14;
insert(h[i],i);
}
for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
for(int i=0;i<=lg[n];i++)
for(int j=1;j<=n;j++){
if(i==0){
f[i][j][0]=secminn[j];
f[i][j][1]=minn[j];
dista[i][j][0]=dsm[j];
dista[i][j][1]=distb[i][j][0]=0;
distb[i][j][1]=dm[j];
}
else if(i==1){
f[i][j][0]=f[0][f[0][j][0]][1];
f[i][j][1]=f[0][f[0][j][1]][0];
dista[i][j][1]=dista[0][j][1]+dista[0][f[0][j][1]][0];
dista[i][j][0]=dista[0][j][0]+dista[0][f[0][j][0]][1];
distb[i][j][1]=distb[0][j][1]+distb[0][f[0][j][1]][0];
distb[i][j][0]=distb[0][j][0]+distb[0][f[0][j][0]][1];
}
else{
f[i][j][0]=f[i-1][f[i-1][j][0]][0];
f[i][j][1]=f[i-1][f[i-1][j][1]][1];
dista[i][j][0]=dista[i-1][j][0]+dista[i-1][f[i-1][j][0]][0];
dista[i][j][1]=dista[i-1][j][1]+dista[i-1][f[i-1][j][1]][1];
distb[i][j][0]=distb[i-1][j][0]+distb[i-1][f[i-1][j][0]][0];
distb[i][j][1]=distb[i-1][j][1]+distb[i-1][f[i-1][j][1]][1];
}
}
}
void getans1(int x,int d){
int da=0,db=0,k=0,u=x;
for(int i=lg[n];i>=0;i--)
if(f[i][u][k]&&dista[i][u][k]+distb[i][u][k]<=d){
d-=dista[i][u][k]+distb[i][u][k];
da+=dista[i][u][k],db+=distb[i][u][k];
if(i==0) k^=1;
u=f[i][u][k];
}
if(x==1||da*minb<db*mina) mina=da,minb=db,minni=x;
else if(da*minb==db*mina&&da*minb!=0&&h[x]>h[minni]) minni=x;
}
void getans(int u,int d){
int da=0,db=0,k=0;
for(int i=lg[n];i>=0;i--)
if(f[i][u][k]&&dista[i][u][k]+distb[i][u][k]<=d){
d-=dista[i][u][k]+distb[i][u][k];
da+=dista[i][u][k],db+=distb[i][u][k];
if(i==0) k^=1;
u=f[i][u][k];
}
cout<<da<<' '<<db<<'\n';
}
signed main(){
std::ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
h[0]=1e14;
cin>>n;
for(int i=1;i<=n;i++) cin>>h[i];
pre();
int x,s;
cin>>x;
for(int i=1;i<n;i++) getans1(i,x);
cout<<minni<<'\n';
cin>>m;
for(int i=1;i<=m;i++){
cin>>s>>x;
getans(s,x);
}
return 0;
}

浙公网安备 33010602011771号