[NOIP2012]开车旅行

开车旅行

  • 关键字:链表,倍增

结果和评价

  • 得分:70

  • 时间:40+60

  • 评价 :代码功底很一般。

我的思路

量化

  • 我们需要处理的就是两个东西。
    • 怎么快速的求出每个点对于的 tA[i] , tB[i] 表示在i点不同的人开车会到达的点。
    • 然后就是怎么高效的模拟出开车过程了。

分析

70
  • 对于第一个问题。我们可以 O(n2) 扫一下
void init(){
  For(x,1,n){
    int a=0,b=0;
    For(j,x+1,n){
      int d=abs(h[j]-h[x]);
      if((!a)||(d<abs(h[a]-h[x]))||(d==abs(h[a]-h[x])&&h[j]<h[a]))b=a,a=j;
      else if((!b)||(d<abs(h[b]-h[x]))||(d==abs(h[b]-h[x])&&h[j]<h[b]))b=j;
    }
    tA[x]=b,tB[x]=a;
  }
}
  • 然后模拟的过程直接 O(n) 模拟地跳即可。
void solve(int x,int limt,bool Ot=1){
    int a=0,b=0;
    bool f=0;/*!f -> A*/
    int cur=x;
    int res=limt;
    while(res){
        if(!f){
            int nxt=tA[cur];
            int d=abs(h[cur]-h[nxt]);
            if(!nxt)break;
            else if(d>res)break;
            else a+=d,res-=d;
            cur=nxt;
        }
        else {
            int nxt=tB[cur];
            int d=abs(h[cur]-h[nxt]);
            if(!nxt)break;
            else if(d>res)break;
            else b+=d,res-=d;
            cur=nxt;
        }
        f=!f;
    }
    if(Ot)pt(a),ptn(b);
    else {
        if(!b)bi[x]=INF;
        else bi[x]=1.0*a/b;/*用于回答第一个询问*/
    }
}   
100
  • 对于第二个过程。可以使用倍增高效优化

    • 定义

    • to[i][j] 表示从 i 出发,A,B都跳 2j1 次回到达的点。

    • cA[i][j] cB[i][j] 分别表示到达该点, A,B 各自需要的里程。

    • 处理

    For(i,1,n){
      to[i][0]=tB[tA[i]];
      cA[i][0]=abs(h[tA[i]]-h[i]);
      cB[i][0]=abs(h[to[i][0]]-h[tA[i]]);
    }
    For(j,1,N)For(i,1,n)to[i][j]=to[to[i][j-1]][j-1];
    For(j,1,N)For(i,1,n)if(to[i][j])cA[i][j]=cA[i][j-1]+cA[to[i][j-1]][j-1];
    For(j,1,N)For(i,1,n)if(to[i][j])cB[i][j]=cB[i][j-1]+cB[to[i][j-1]][j-1];
  • 然后每次跳跃就可以做到 log(n)

    • void solve(int x,int limt,bool Ot=1){
      int s=x;
      ll a=0,b=0;
      Ror(i,0,N){
          if(to[x][i]&&a+b+cA[x][i]+cB[x][i]<=limt)
              a+=cA[x][i],b+=cB[x][i],x=to[x][i];
      }
      
      int to=tA[x];
      if(to){
          int d=abs(h[x]-h[to]);
          if(d+a+b<=limt)a+=d;
      }
      if(Ot)pt(a),ptn(b);
      else {
          if(!b)bi[x]=INF;
          else {
              bi[s]=1.0*a/b;
          }
      }
      }
  • 好了,我们现在需要的就是如何高效的求 tA,tB 数组了。。。怎么搞

    • 量化一下
    • 对于一个 x 我们需要求出的其实就是在[x+1,n]范围里 abs(h[x]h[i]) 最小的几个。
    • 很直观的,可以使用 set ,从 n 1扫过了,然后每次 bound 一下取出需要的元素

    我们™的比赛的时候写了 set ,没有搞出来,赛后调了1个多小时也可以调出来。。恶心了

    • 其实可以使用简单的双向链表来实现这一操作的。
    • 因为我们只要求出 x 权值上的前驱和后继即可
    • 所以,我们可以先排序,然后O(n)维护。
    • 我们用 rnk[i] 表示权值第 i 小的点,然后用ord[i]表示第 i 个点排第几即可。
    • 然后从左向右扫,每次询问完了以后把自己从链表中删除即可。
void init(){
    For(i,1,n)rnk[i]=i;
    sort(rnk+1,rnk+1+n,cmp);
    For(i,1,n)ord[rnk[i]]=i;
    For(i,1,n)bug(i),debug(rnk[i]);
    For(i,1,n-1)nxt[i]=i+1;
    For(i,2,n)pre[i]=i-1;
    For(i,1,n){
        int x=ord[i];
        check(i,rnk[pre[x]]);
        check(i,rnk[pre[pre[x]]]);
        check(i,rnk[nxt[x]]);
        check(i,rnk[nxt[nxt[x]]]);
        if(pre[x])nxt[pre[x]]=nxt[x];
        if(nxt[x])pre[nxt[x]]=pre[x];
    }

    For(i,1,n){
        to[i][0]=tB[tA[i]];
        cA[i][0]=abs(h[tA[i]]-h[i]);
        cB[i][0]=abs(h[to[i][0]]-h[tA[i]]);
    }
    For(j,1,N)For(i,1,n)to[i][j]=to[to[i][j-1]][j-1];
    For(j,1,N)For(i,1,n)if(to[i][j])cA[i][j]=cA[i][j-1]+cA[to[i][j-1]][j-1];
    For(j,1,N)For(i,1,n)if(to[i][j])cB[i][j]=cB[i][j-1]+cB[to[i][j-1]][j-1];
}

实现

#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<time.h>
#include<math.h>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<bitset>
#include<map>
#include<string>

using namespace std;

#define bug(x) cerr<<#x<<'='<<x<<' '
#define debug(x) cerr<<#x<<'='<<x<<'\n'
#define For(i,a,b) for(int i=a;i<=b;++i)
#define Ror(i,a,b) for(int i=b;i>=a;--i)

typedef long long ll;

template<class T>void rd(T&x){
    x=0;char c,f=1;
    while(c=getchar(),!isdigit(c))if(c=='-')f=-1;
    do x=(x<<3)+(x<<1)+(c^'0');
    while(c=getchar(),isdigit(c));
    x*=f;
}

template<class T>void pf(T x){
    static int top=0,stk[100];
    if(!x)putchar('0');
    if(x<0)putchar('-'),x=-x;
    while(x)stk[++top]=x%10,x/=10;
    while(top)putchar(stk[top--]+'0');
}

template<class T>void pt(T x){pf(x);putchar(' ');}
template<class T>void ptn(T x){pf(x);putchar('\n');}
template<class T>void Max(T&x,T y){if(x<y)x=y;}
template<class T>void Min(T&x,T y){if(y<x)x=y;}

const int M=100005,N=19;

const double INF=1e9;

ll h[M],tA[M],tB[M],to[M][20],cA[M][20],cB[M][20],n,m;

double bi[M];

struct P{
    int h,id;
    bool operator<(const P&A)const{
        if(h!=A.h)return h<A.h;
        return id<A.id;
    }
};

multiset<P>S;
multiset<P>::iterator it1,it2,it3,it4;

int rnk[M],nxt[M],pre[M],ord[M];

bool cmp(int a,int b){return h[a]<h[b];}

void check(int x,int j){
    if(!j)return;
    ll &b=tA[x],&a=tB[x];
    int d=abs(h[j]-h[x]);
    if((!a)||(d<abs(h[a]-h[x]))||(d==abs(h[a]-h[x])&&h[j]<h[a]))b=a,a=j;
    else if((!b)||(d<abs(h[b]-h[x]))||(d==abs(h[b]-h[x])&&h[j]<h[b]))b=j;
}

struct P2{
void init(){
    For(i,1,n)rnk[i]=i;
    sort(rnk+1,rnk+1+n,cmp);
    For(i,1,n)ord[rnk[i]]=i;
    For(i,1,n)bug(i),debug(rnk[i]);
    For(i,1,n-1)nxt[i]=i+1;
    For(i,2,n)pre[i]=i-1;
    For(i,1,n){
        int x=ord[i];
        check(i,rnk[pre[x]]);
        check(i,rnk[pre[pre[x]]]);
        check(i,rnk[nxt[x]]);
        check(i,rnk[nxt[nxt[x]]]);
        if(pre[x])nxt[pre[x]]=nxt[x];
        if(nxt[x])pre[nxt[x]]=pre[x];
    }

    For(i,1,n){
        to[i][0]=tB[tA[i]];
        cA[i][0]=abs(h[tA[i]]-h[i]);
        cB[i][0]=abs(h[to[i][0]]-h[tA[i]]);
    }
    For(j,1,N)For(i,1,n)to[i][j]=to[to[i][j-1]][j-1];
    For(j,1,N)For(i,1,n)if(to[i][j])cA[i][j]=cA[i][j-1]+cA[to[i][j-1]][j-1];
    For(j,1,N)For(i,1,n)if(to[i][j])cB[i][j]=cB[i][j-1]+cB[to[i][j-1]][j-1];
}

    void solve(int x,int limt,bool Ot=1){
        int s=x;
        ll a=0,b=0;
        Ror(i,0,N){
            if(to[x][i]&&a+b+cA[x][i]+cB[x][i]<=limt)
                a+=cA[x][i],b+=cB[x][i],x=to[x][i];
        }

        int to=tA[x];
        if(to){
            int d=abs(h[x]-h[to]);
            if(d+a+b<=limt)a+=d;
        }
        if(Ot)pt(a),ptn(b);
        else {
            if(!b)bi[x]=INF;
            else {
                bi[s]=1.0*a/b;
            }
        }
    }
    void work(){
        init();
        int k=0,x,s;
        rd(x);
        For(i,1,n)solve(i,x,0);
        For(i,1,n)if(!k||(bi[k]>bi[i])||((bi[k]==bi[i])&&h[k]<h[i]))k=i;      
        rd(m);
        ptn(k);
        For(i,1,m){
            rd(s),rd(x);
            solve(s,x);
        }
    }
}P2;

int main(){
//  freopen("drive.in","r",stdin);
//  freopen("drive.out","w",stdout);
    rd(n);
    For(i,1,n)rd(h[i]);
    P2.work();
    return 0;
}

反思

  • stl有毒的,没有很好的代码功底。。不要轻易使用。
  • stl 的复杂度都比较大,而且可能容易出错的,如果可以使用其他的算法,竟可能用其他的算法
  • 总之, stl 是最后没有办法的办法。。。了

其实,还是自己太辣鸡了。。。。T_T

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值