倍增+双向链表——洛谷P1081 开车旅行

来源:互联网 发布:java开发面试题及答案 编辑:程序博客网 时间:2024/05/16 09:41

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();}
1 0
原创粉丝点击