[倍增][双向链表] Jzoj P3101 开车旅行
题解
- 显然可以发现,我们可以将每个点的最近和次近预处理出来
- 对于第一问,直接枚举出发点,暴力跑
- 对于第二问,就是暴力跑
- 时间复杂度是O(2N^2+NM),这样的话可以过70%
- 那么考虑,先把高度从小到大排序的位置记录下来,然后用一个双向链表将他们存起来
- 那么对于B的选择,就是左边第一个还是右边第一个近,谁近就选谁
- 那么A的选择,假设A先选了左边第一个,那么A的就要选择左边第二个或者是右边第一个中最近的
- 假设A先选了右边第一个,那就是在左边第一个和右边第二个中找最近的
- 那么预处理的话,我们就可以用倍增的思想来做
- 这样的话,查询也是要用倍增的
- 最后的时间复杂度是O(N log N+M log N)
代码
1 #include <cstdio> 2 #include <cmath> 3 #include <algorithm> 4 #define ll long long 5 #define N 100010 6 using namespace std; 7 int n,x0,d1,d2,num=1,Q,x,y,h[N],w1[N],w2[N],d[N],left[N],right[N],f[N][20]; 8 ll dis1[N],dis2[N],g[N][20],k[N][20]; 9 bool cmp(int a,int b){ return h[a]<h[b]; } 10 void pd(int x,int y) 11 { 12 if (!y) return; 13 int r=abs(h[x]-h[y]); 14 if (r<dis1[x]||r==dis1[x]&&h[y]<h[w1[x]]) dis2[x]=dis1[x],w2[x]=w1[x],dis1[x]=r,w1[x]=y; 15 else if (r<dis2[x]||r==dis2[x]&&h[y]<h[w2[x]]) dis2[x]=r,w2[x]=y; 16 } 17 void init() 18 { 19 sort(d+1,d+n+1,cmp),dis1[0]=dis2[0]=10000000000; 20 for (int i=1;i<=n;i++) left[d[i]]=d[i-1],right[d[i]]=d[i+1],dis1[i]=dis2[i]=10000000000; 21 for (int i=1;i<=n;i++) 22 { 23 int x2=left[left[i]],x1=left[i],y2=right[right[i]],y1=right[i]; 24 pd(i,x2); 25 if (x1!=x2) pd(i,x1); 26 if (y1!=x1&&y1!=x2) pd(i,y1); 27 if (y2!=y1&&y2!=x1&&y2!=x2) pd(i,y2); 28 right[left[i]]=right[i],left[right[i]]=left[i]; 29 } 30 for (int i=1;i<=n;i++) f[i][0]=w1[w2[i]],g[i][0]=dis2[i],k[i][0]=dis1[w2[i]]; 31 for (int i=1;i<=log2(n/2);i++) for (int j=1;j<=n;j++) f[j][i]=f[f[j][i-1]][i-1],g[j][i]=g[j][i-1]+g[f[j][i-1]][i-1],k[j][i]=k[j][i-1]+k[f[j][i-1]][i-1]; 32 } 33 void get(int x,int dis) 34 { 35 for (int i=(int)(log2(n/2));i>=0;i--) 36 if (f[x][i]&&f[x][i]<=n&&dis>=g[x][i]+k[x][i]) 37 d1+=g[x][i],d2+=k[x][i],dis-=g[x][i]+k[x][i],x=f[x][i]; 38 if (w2[x]&&w2[x]<=n&&dis>=dis2[x]) d1+=dis2[x]; 39 } 40 int main() 41 { 42 scanf("%d",&n); 43 for (int i=1;i<=n;i++) scanf("%d",&h[i]),d[i]=i; 44 init(),scanf("%d",&x0); 45 double ans=10000000000; 46 for (int i=1;i<=n;i++) 47 { 48 d1=d2=0,get(i,x0); 49 double p=d1*1.0/d2; 50 if (p<ans||p==ans&&h[i]>h[num]) num=i,ans=p; 51 else if (!d2&&ans==10000000000&&h[i]>h[num]) num=i; 52 } 53 printf("%d\n",num),scanf("%d",&Q); 54 while (Q--) 55 { 56 d1=d2=0,scanf("%d%d",&x,&y),get(x,y); 57 printf("%d %d\n",d1,d2); 58 } 59 }