倍增+双向链表——洛谷P1081 开车旅行
https://daniu.luogu.org/problem/show?pid=1081#sub
提高组的第三题;
哇;
首先题目很恶心;
然而发现也没什么歧义,也不难看懂;
70分的暴力做法;
n*n预处理出A[i],B[i]两个数组;
标表示在i点时小A和小B开车分别会走到哪里;
这个只要暴力高就好了;
指的一提的是负最大初始值1e9不够;
要3e9;
然后我们就可以发现也已搞一下优化;
我们把读入排序;
拍好后搞成一个双向链表;
然后按照点的先后顺序去枚举;
对于k点;
显然A[i],B[i];
出自双向链表的
k-1,k-2,k+1,k+2里面;
我们统计号好k之后直接把k删掉;
这样对于后面的k,链表里面的非k点的顺序一定比k后;
这样预处理时间就变成了nlogn;
然后我们把小A一天+小B一天合为一步;
用这个去搞倍增表;
然后直接像lca一样去倍增就好啦;
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int N=1e5+100;
struct L2{
int v,l,r,num;//双向链表
}L[N];
struct zz{//一个答案结构体
int a,b;
zz(){a=b=0;}
};
int a[N],A[N],B[N],c[N];
int f[N][20],AA[N][20],BB[N][20];//倍增表,AA BB是这段倍增区间A B的花费
int x,y,z,n,m;
bool cmp(L2 a,L2 b){return a.v<b.v;}
void make(int k){
int mst=3e9,med=3e9,aa=0,bb=0,v,m;
m=L[k].l;
if(m){
v=abs(L[k].v-L[m].v);
if(v<mst||(v==mst&&L[k].v>L[m].v)){
med=mst; bb=aa; mst=v; aa=L[m].num;
}else
if(v<med||(v==med&&L[k].v>L[m].v)){
med=v; bb=L[m].num;
}
m=L[m].l;
if(m){
v=abs(L[k].v-L[m].v);
if(v<mst||(v==mst&&L[k].v>L[m].v)){
med=mst; bb=aa; mst=v; aa=L[m].num;
}else
if(v<med||(v==med&&L[k].v>L[m].v)){
med=v; bb=L[m].num;
}
}
}
m=L[k].r;
if(m){
v=abs(L[k].v-L[m].v);
if(v<mst||(v==mst&&L[k].v>L[m].v)){
med=mst; bb=aa; mst=v; aa=L[m].num;
}else
if(v<med||(v==med&&L[k].v>L[m].v)){
med=v; bb=L[m].num;
}
m=L[m].r;
if(m){
v=abs(L[k].v-L[m].v);
if(v<mst||(v==mst&&L[k].v>L[m].v)){
med=mst; bb=aa; mst=v; aa=L[m].num;
}else
if(v<med||(v==med&&L[k].v>L[m].v)){
med=v; bb=L[m].num;
}
}
}
A[L[k].num]=bb;//注意,我的写法比较怪异,A和aa翻一下的
B[L[k].num]=aa;
L[L[k].r].l=L[k].l;//删除
L[L[k].l].r=L[k].r;
}
void makebz(){
for(int i=1;i<=n;i++){
f[i][0]=B[A[i]];
AA[i][0]=abs(a[i]-a[A[i]]);
BB[i][0]=abs(a[A[i]]-a[B[A[i]]]);
}
for(int j=1;(1<<j)<=n;j++)
for(int i=1;i<=n;i++)
if(f[i][j-1]){
f[i][j]=f[f[i][j-1]][j-1];
AA[i][j]=AA[i][j-1]+AA[f[i][j-1]][j-1];
BB[i][j]=BB[i][j-1]+BB[f[i][j-1]][j-1];
}
}
zz work(int now,int x){
int aa=0,bb=0,j;
while(1){
j=0;
for(;f[now][j]&&(aa+AA[now][j]+bb+BB[now][j]<=x);j++);
if(!j)break;
j--;
aa+=AA[now][j];
bb+=BB[now][j];
now=f[now][j];
}
if(A[now]&&(aa+bb+abs(a[A[now]]-a[now])<=x))aa+=abs(a[A[now]]-a[now]);
zz ans; //最后A可能还会走一下
ans.a=aa;
ans.b=bb;
return ans;
}
void outit1(){
int x,ans;
double Ans=1e9+5,temp;
zz c;
scanf("%d",&x);
for(int i=1;i<=n;i++){
c=work(i,x);
if(!c.b)temp=1e9;else temp=(double)c.a/c.b;
if(Ans>temp||(Ans==temp&&a[ans]<a[i]))Ans=temp,ans=i;
}
printf("%d\n",ans);
}
void outit2(){
int m,x,y;
zz c;
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
c=work(x,y);
printf("%d %d\n",c.a,c.b);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),L[i].v=a[i],L[i].num=i;
sort(L+1,L+n+1,cmp);
for(int i=1;i<=n;i++){//创建一个链表
L[i].l=i-1;
L[i].r=i+1;
c[L[i].num]=i;
}
L[n].r=0;
for(int i=1;i<=n;i++)make(c[i]);//按顺序来处理点
makebz();//做倍增表
outit1();
outit2();
}