我对状态空间的理解:https://www.cnblogs.com/AKMer/p/9622590.html
题目传送门:https://www.luogu.org/problemnew/show/P3407
虽然这道题值域范围很大,但是(n)还在可以接受的范围。
我们灵性的理解一下,如果一个人运气不好,碰不到其他人,他将一直走下去。所以我们就可以在(O(1))的时间内算出他最后的位置。
我们再灵性的想一下,如果一个人碰到了人群,那么他就将停止走路,在人群中一起聊天。
人群个数不会超过(n/2),这很显然对吧。我们只要掌握每一个会形成人群的点,就可以算出所有人最后的位置了。
然后某两个人可以碰到一起当且仅当(i)号人向东走且(i+1)号人向西走,(pos[i+1]-pos[i]leqslant2*T),那样就会在(pos[i]+(pos[i+1]-pos[i])/2)的地方形成人群。这时我们再去更新上一个人群到这一个人群中向东走的人的答案,因为新人群的出现只会影响这满足上述条件的人。然后向西走的就直接算会不会走到上一个人群里,不会的话就直接走,会的话上一个人群的位置就是他最后的位置。
时间复杂度:(O(n))
空间复杂度:(O(n))
代码如下:
#include <cstdio>
#include <algorithm>
using namespace std;
#define ll long long
const int maxn=1e5+5;
int di[maxn];//记录每个人的方向
int n,Q,now=1;//now是上一个人群后的第一个人的标号
ll T,lst=-3e18;//T记录时间,lst记录上一个人群的位置
ll pos[maxn],POS[maxn];//pos记录初始位置,POS记录最后位置
int main() {
scanf("%d%lld%d",&n,&T,&Q);
for(int i=1;i<=n;i++)
scanf("%lld%d",&pos[i],&di[i]);//读入
for(int i=1;i<=n;i++) {
if(di[i]==2) POS[i]=max(pos[i]-T,lst);//如果是向西走那么直接对pos[i]-T,lst取max
else {
POS[i]=pos[i]+T;//我不管最终位置是啥,先放到pos[i]+T再说。到时候形成新人群了会回过头来更新
if(di[i+1]==2&&pos[i+1]-pos[i]<=2*T) {//如果会形成新人群
lst=pos[i]+(pos[i+1]-pos[i])/2;//更新人群位置
while(now<i+2) {
if(di[now]==1)POS[now]=min(pos[now]+T,lst);
now++;//把上一个人群到当前人群间所有向东走的人的答案都试着更新一遍。
}
}
}
}
for(int i=1;i<=Q;i++) {
int x;scanf("%d",&x);
printf("%lld
",POS[x]);
}
return 0;
}