luogu P3407 散步 解题报告

流过昼夜 提交于 2019-11-29 21:25:15

 

题目描述

 

一条道路上,位置点用整数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\n", 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\n", posx+t);
            else if(s[mid] < posx) printf("%lld\n", posx+t);
            else if(s[mid] < posx+t && s[mid] > posx) printf("%lld\n", s[mid]);
            else printf("%lld\n", s[mid]);
        }
        if(flag == 2){
            if(s[mid] < posx-t && s[mid] < posx) printf("%lld\n", posx-t);
            else if(s[mid] > posx-t && s[mid] < posx) printf("%lld\n", s[mid]);
            else if(s[mid] > posx) printf("%lld\n", posx-t); 
            else printf("%lld\n", s[mid]);
        }
    }
    return 0;
}
View Code

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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!