题目描述
一条道路上,位置点用整数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;
}
但是,这不是重点,这道题还有一个非正解的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;
}
好了,这道题就这么愉快地结束了。