[NOIP2012] 开车旅行

[NOIP2012] 开车旅行

题目大意:在数轴上有\(n\)个不同高度的城市从西到东排列,定义\(dis(i,j)\)\(i\)城市到\(j\)城市的\(abs(h_i,h_j)\),有两个人\(A\)\(B\)轮流开车从任意城市出发向东旅行,\(A\)一定是先开车的,他会选择去距离当前城市第二近的城市(\(dis\)一样,去高度低的城市),走一定的里程,那么第二次\(B\)在开车,他会选择去距离当前的城市最近的城市(\(dis\)一样,去高度低的城市),走一定的里程;如果对于\(A,B\)里程不够了,或者没有可以选的城市了,结束旅行.现在有两个询问

  1. 无论从哪个地方出发,给定里程数,求\(A,B\)开车里程的最小比值所在的旅行的出发地,如果有多个出发地的比值相同,取海拔高的
  2. 给定\(m\)组询问\(S_i,X_i\),求从\(S_i\)出发,不能超过\(X_i\)的里程的旅行中,\(A\)\(B\)分别走过的里程值

Solution.70pts

数据范围比较小,模拟可过,不要什么图都拿邻接表存,然后我的深搜就写挂了

Code.70pts

一些操作还是很巧妙的

#include <iostream>
#include <cmath>
#include <cstdio>

const int N = 1005;

int n, x0;
int h[N], dis[N][N], one[N], two[N];
//学一下自己写abs
int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) scanf("%d", &h[i]);
    for(int i = 1; i <= n; ++i){
        for(int j = i + 1; j <= n; ++j){
            dis[i][j] = abs(h[i] - h[j]);
            if(!one[i]){
                one[i] = j;
            }else if(dis[i][one[i]] > dis[i][j] || (dis[i][one[i]] == dis[i][j] && h[one[i]] > h[j])){
                two[i] = one[i];
                one[i] = j;
            }else if(!two[i]){
                two[i] = j;
            }else if(dis[i][two[i]] > dis[i][j] || (dis[i][two[i]] == dis[i][j] && h[two[i]] > h[j])){
                two[i] = j;
            }
        }
    }
    int x, a, b, c, ans;
    scanf("%d", &x);
    double tmp = 2147483647;
    for(int i = 1; i <= n - 2; ++i){//n - 1 和 n 的旅程还没开始就结束了
        a = b = 0;
        c = i;
        for(int j = 1; ; ++j){
            if(j & 1){
                if(!two[c] || dis[c][two[c]] + a + b > x) break;
                a += dis[c][two[c]], c = two[c];
            }else{
                if(!one[c] || dis[c][one[c]] + a + b > x) break;
                b += dis[c][one[c]], c = one[c];
            }
        }
        if(b && a * 1.0 / b < tmp) tmp = a * 1.0 / b, ans = i;
        else if(b && a * 1.0 / b == tmp){
            if(h[ans] < h[i])
                ans = i;
        }
    }
    printf("%d\n", ans);
    int m; 
    scanf("%d", &m);
    while(m--){
        scanf("%d %d", &c, &x);
        a = b = 0;
        for(int j = 1; ; ++j){
            if(j & 1){
                if(!two[c] || dis[c][two[c]] + a + b > x) break;
                a += dis[c][two[c]], c = two[c];
            } else {
                if(!one[c] || dis[c][one[c]] + a + b > x) break;
                b += dis[c][one[c]], c = one[c];
            }
        }
        printf("%d %d\n", a, b);
    }
    return 0;
}

Solution.100pts

排序后运用双向链表\(O(n)\)处理\(one[i]\)\(two[i]\),链表上的序号就代表了排序后的城市排名

为了方便,直接把\(i-1,i+1,i-2,i+2\)排序取前两名,然后倍增的搞,转移也方便

Attention.100pts

  • \(prepare\)函数按照相对值的绝对值排序
  • 双向链表里面删去结点,注意要直接将前驱后继连接,不可以变成\(i-1\)\(i+1\),因为很可能\(i-1\)\(i+1\)也是一个被删除的点
  • 十分注意快读中位运算的优先级

Code.100pts

#include <iostream>
#include <cstdio>
#include <algorithm>
#define pf(x) printf("%d\n", x)
using std::sort;

const int N = 1e5 + 10;

int n;
int pre[N], nxt[N], head[N], one[N], two[N], data[N];
int A[N][22], B[N][22], f[N][22];
    
int abs(int x) {
    if (x >= 0) return x; return -x;
}
    
struct City{
    int p, i;
    bool operator < (const City &cmp) const {
        return p < cmp.p;
    }
}h[N];

struct QWQ{
    int p, i;
    bool operator < (const QWQ &cmp) const {
        if(p == cmp.p){
            return data[i] < data[cmp.i];
        }
        return p < cmp.p;
    }
};

inline int read(){
    int x = 0, f = 1;
    char c = getchar();
    while(c < '0' || c > '9'){
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9'){ 
        x = (x << 3) + (x << 1) + (c ^ 48);//不括起来就出现了傻逼现象 
        c = getchar();
    }
    return x * f;
}

inline void get_dis(int af, int bf){
    QWQ qwq[5];
    int qwq_cnt = 0;
    if(pre[af]) {
        qwq[++qwq_cnt].p = abs(h[pre[af]].p - data[bf]);
        qwq[qwq_cnt].i = h[pre[af]].i;
        if(pre[pre[af]]){
            qwq[++qwq_cnt].p = abs(h[pre[pre[af]]].p - data[bf]);
            qwq[qwq_cnt].i = h[pre[pre[af]]].i;
        }
        nxt[pre[af]] = nxt[af];//delete
    }
    if(nxt[af]){
        qwq[++qwq_cnt].p = abs(h[nxt[af]].p - data[bf]);
        qwq[qwq_cnt].i = h[nxt[af]].i;
        if(nxt[nxt[af]]){
            qwq[++qwq_cnt].p = abs(h[nxt[nxt[af]]].p - data[bf]);
            qwq[qwq_cnt].i = h[nxt[nxt[af]]].i;
        }
            
        pre[nxt[af]] = pre[af];//delete pre[nxt[af]] = af - 1;
    }
    sort(qwq + 1, qwq + qwq_cnt + 1);
    if(qwq_cnt >= 1)one[bf] = qwq[1].i;
    if(qwq_cnt >= 2)two[bf] = qwq[2].i;
    return;
}

inline void prepare(){
    for(int i = 1; i <= n; ++i){
        if(two[i]) A[i][0] = abs(data[two[i]] - data[i]);
        if(one[two[i]]) B[i][0] = abs(data[one[two[i]]] - data[two[i]]);
        if(one[two[i]])f[i][0] = one[two[i]];
    }
}

int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i){
        h[i].p = read();
        h[i].i = i;
        data[i] = h[i].p;
    }
    sort(h + 1, h + n + 1);
    for(int i = 1; i <= n; ++i){
        head[h[i].i] = i;
        nxt[i] = i + 1;
        pre[i] = i - 1;
        if(i == n)
            nxt[i] = 0;
    }
    for(int i = 1; i <= n; ++i)
        get_dis(head[i], i);
    prepare();
    for(int i = 1; i <= 17; ++i){
        for(int j = 1; j <= n; ++j){
            A[j][i] = A[j][i - 1] + A[f[j][i - 1]][i - 1];
            B[j][i] = B[j][i - 1] + B[f[j][i - 1]][i - 1];
            f[j][i] = f[f[j][i - 1]][i -1];
        }
    }
    int x0, a, b, c, z, ans;
    double tmp = 2147483640;
    
    scanf("%d", &x0);
    for(int i = 1; i <= n; ++i){
        a = b = 0, z = x0, c = i;
        for(int j = 20; j >= 0; --j){           
            if(f[c][j] && z >= A[c][j] + B[c][j]){
                a += A[c][j];
                b += B[c][j];
                z -= A[c][j] + B[c][j];
                c = f[c][j];
            }
        }
        if(A[c][0] <= z) a += A[c][0];
        if(b && a * 1.0 / b < tmp) {
            tmp = a * 1.0 / b, ans = i;
        }
        else if(b && a * 1.0 / b == tmp){
            if(data[ans] < data[i])
                ans = i;
        }
    }
    printf("%d\n", ans);
    int m;
    scanf("%d", &m);
    while(m--){
        scanf("%d %d", &c, &x0);
        z = x0, a = b = 0;
        for(int j = 20; j >= 0; --j){           
            //printf("c = %d j = %d f[c][j] = %d  z = %d a[c][j] = %d b[c][j] = %d \n", c, j, f[c][j], z, A[c][j], B[c][j]);
            if(f[c][j] && z >= A[c][j] + B[c][j]){
                a += A[c][j];
                b += B[c][j];
                z -= A[c][j] + B[c][j];
                c = f[c][j];
            }
        }
        if(A[c][0] <= z) a += A[c][0];
        printf("%d %d\n", a, b);
    }
    return 0;
}

转载于:https://www.cnblogs.com/LMSH7/p/9605605.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值