• luogu P3407 散步 二分答案


    题目描述

    一条道路上,位置点用整数A表示。

    当A=0时,有一个王宫。当A>0,就是离王宫的东边有A米,当A<0,就是离王宫的西边有A米。

    道路上,有N个住宅从西向东用1-N来标号。每个住宅有一个人。住宅只会存在于偶数整数点。

    该国国王认为,国民体质下降,必须要多运动,于是下命令所有人都必须出门散步。所有的国民,一秒钟可以走1米。每个国民各自向东或者向西走。这些方向你是知道的。命令发出后所有人同时离开家门开始散步。

    然而该国的国民个都很健谈,如果在散步途中两个人相遇,就会停下来交谈。正在走路的人碰到已经停下来的人(重合)也会停下来交谈。一但停下来,就会聊到天昏地暗,忘记了散步。

    现在命令已经发出了T秒,该国有Q个重要人物,国王希望能够把握他们的位置。你能帮他解答吗?

    输入格式

    第一行是3个整数,N,T,Q

    接下来N行,每行两个整数Ai,Ri。Ai是家的坐标,如果Ri是1,那么会向东走,如果是2,向西。数据保证Ai是升序排序,而且不会有两个人初始位置重合。

    接下来Q行,每行一个整数,表示国王关心的重要人物。

    输出格式

    Q行,每行一个整数,表示这个人的坐标。

    20%数据 N<=100,T<=10000

    另外20%数据 N<=5000

    另外20%数据 从最西边数起连续的若干国民全部往东,剩下的全部往西

    100%数据 Ai为偶数,|Ai|<=10^18,|T|<=10^18,1<=Q<=N<=100000.

    ----------------------------------------我是分割线---------------------------------------------------------------

    题面大意:数轴上有n个人,每秒钟在给定的方向(向东或向西)移动一个距离,当一个人与一个人相遇时两人不再移动,求t秒后,指定m个人所在的位置。

    看到这道题的第一眼,估计你们应该都想出正解了,这题的正解应该是比较好想的。

    对于每一个行走的人(以向西走的为例),最终停下来无非三种情况:

    1,时间到了,不能再走了。

    2,和自己西边向东走的人相遇了。

    3,自己西边的有人相遇了,自己遇上了相遇的人。

    对于最西边向西走的,可以不用管他。

    同理可以判定向东走的人。分类讨论就行了。

    时间复杂度O(N)。

    贴上代码方便理解:

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    int n, Q, q[N];
    long long ans[N], p[N][2], t;
    inline ll read(){
        ll x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    int main(){
        n = read(); t = read(); Q = read();
        rep(i, 1, n) p[i][0] = read(), p[i][1] = read();
        rep(i, 1, Q) q[i] = read();     
        rep(i, 1, n)
            if(p[i][1] == 2){
                if(i == 1)
                    ans[i] = p[i][0] - t;
                else if(p[i-1][1] == 2)
                    ans[i] = max(ans[i-1], p[i][0]-t);
                else ans[i] = max(p[i][0]/2+p[i-1][0]/2, p[i][0]-t);
            }
        per(i, n, 1)   
            if(p[i][1] == 1){
                if(i == n)
                    ans[i] = p[i][0]+t;
                else if(p[i+1][1] == 1)
                    ans[i] = min(ans[i+1], p[i][0]+t);
                else ans[i] = min(p[i][0]/2+p[i+1][0]/2, p[i][0]+t);
            }
        rep(i, 1, Q) printf("%d
    ", ans[q[i]]);
        return 0;
    }
    View Code

    但是,这不是重点,这道题还有一个非正解的O(N logN)的做法,常数巨小,跑的比上面的O(N)正解还要快!

    思路:预处理出每一个相遇点,与当位置+或者-t,看看是否会到达最近的相遇点,如果能就输出相遇点,否则输出pos+t 或者pos-t.最近的相遇点可以二分查找,细节有点多,需要额外注意。

    code:

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    const ll inf = 4557430888798830399;
    ll n, t, q;
    ll s[N], k = 0;
    struct people{ll pos, dic, id; } a[N];
    bool mycmp(people a, people b) {return a.pos < b.pos; }
    inline ll read(){
        ll x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    int main(){
        n = read(); t = read(); q = read();
        rep(i, 1, n) a[i].pos = read(), a[i].dic = read(), a[i].id = i;
        sort(a+1, a+n+1, mycmp);
        rep(i, 2, n){
            if(a[i].dic == 2 && a[i-1].dic == 1) s[++k] = (a[i].pos+a[i-1].pos)>>1;
        }  
        s[0] = -inf, s[k+1] = inf;
        rep(i, 1, q){
            ll x = read();
            ll posx = a[a[x].id].pos, flag = a[a[x].id].dic;
            ll l = 1, r = k, mid;
            while(l < r){
                mid = (l+r)>>1;
                if(flag == 1){
                    if(s[mid] < posx) l = mid+1;
                    if(s[mid] > posx) r = mid;
                }
                if(flag == 2){
                    if(mid == l) mid++;
                    if(s[mid] < posx) l = mid;
                    if(s[mid] > posx) r = mid-1;
                }
            }
            mid = l;
            if(s[mid] < posx && flag == 2){
                if(s[mid+1] < posx && s[mid+1] != inf) mid++;
            }
            if(s[mid] > posx && flag == 1){
                if(s[mid-1] > posx && s[mid-1] != inf) mid--; 
            }
            if(flag == 1){
                if(s[mid] > posx+t && s[mid] > posx) printf("%lld
    ", posx+t);
                else if(s[mid] < posx) printf("%lld
    ", posx+t);
                else if(s[mid] < posx+t && s[mid] > posx) printf("%lld
    ", s[mid]);
                else printf("%lld
    ", s[mid]);
            }
            if(flag == 2){
                if(s[mid] < posx-t && s[mid] < posx) printf("%lld
    ", posx-t);
                else if(s[mid] > posx-t && s[mid] < posx) printf("%lld
    ", s[mid]);
                else if(s[mid] > posx) printf("%lld
    ", posx-t); 
                else printf("%lld
    ", s[mid]);
            }
        }
        return 0;
    }
    View Code

    好了,这道题就这么愉快地结束了。

  • 相关阅读:
    动态与静态Include
    java回收算法
    reflection是如何工作的。
    hashcode和equals的约定关系如下
    Java调试器
    混合赋值运算符的使用
    Spring
    Math.?
    oracle--触发器(转)
    oracle --游标详解(转)
  • 原文地址:https://www.cnblogs.com/smilke/p/11533230.html
Copyright © 2020-2023  润新知