luogu解题报告:P3407散步【栈】【奇技淫巧】
分析
定理一:如果从西向东顺次有A,B,C三人,再A与C发生相遇(如果会)之前,必然会有B先和A或B先和C相遇。这是显然的。换句话说,不会出现跨过某人发生相遇。这是一个明显的操作具有顺序性的提示。
定理二:把若干个运动状态相同的人看作一“组”,则每一组有且只有三种情况:静止,向西运动,向东运动。
定理三:对于任意相邻的两个组,他们相遇当且仅当他们相向而行或一个静止一个朝向另一个运动,且运动时间少于T。如果相向而行,相遇点在两组正中;否则在静止的那一组。
至此可以有一个大概的思路:维护一种元素A,包含其位置,运动状态,人。维护一个此元素栈,所有人开始视为独立的元素,自西向东按顺序入栈,如果一个元素A等待入栈:
- 栈为空,则入栈
- 栈顶元素与A在给定时间内不相遇,入栈
- 栈顶元素与A在给定时间内相遇,将栈顶元素出栈,并在两者中点新建一个组,人为原先两组的人的并,标记为静止组。将栈顶元素和A中所有的人的位置更改为新位置。
由于定理1,该方法必然会使所有相遇“顺序”发生,因而不会出现抢人的问题。
人处于哪一组可以用并查集实现,注意对于无法与其他元素相遇的元素,输出时要特别处理。复杂度通过摊还分析,容易得知为
示例代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
using namespace std;
typedef long long ll;
int fa[100005];
inline int father(int i)
{
return fa[i]?fa[i] = father(fa[i]):i;
}
inline void link(int i, int j)
{
if (father(i) != father(j))
fa[father(i)] = father(j);
}
ll pos[100005];
struct sb {
ll first;
int second;
int pos;
void print()
{
printf("-- %lld %d %d --\n", first, second, pos);
}
};
sb make_p(ll a, int b, int c)
{
sb n;
n.first = a;
n.second = b;
n.pos = c;
//cout << n.first << " " << n.second << " " << n.pos << endl;
//n.print();
return n;
}
sb sbs[100005];
sb sav[100005];
int did[100005];
ll n, t, q;
stack<sb> stk;
bool meet(const sb &a, const sb &b)
{
if ((a.second == 0 && b.second == 2 && b.first - a.first <= t)
|| (a.second == 1 && b.second == 0 && b.first - a.first <= t)
|| (a.second == 1 && b.second == 2 && b.first - a.first <= 2*t)
) return true;
return false;
}
ll place(const sb &a, const sb &b)
{
if (a.second == 0) return a.first;
if (b.second == 0) return b.first;
return (a.first+b.first)>>1;
}
int main()
{
memset(fa, 0, sizeof fa);
memset(did, 0, sizeof did);
make_p(1, -2, 3);
scanf("%lld%lld%lld", &n, &t, &q);
for (int i = 1; i <= n; i++) {
ll a; int b;
scanf("%lld%d", &a, &b);
sbs[i] = make_p(a, b, i);
sav[i] = sbs[i];
//make_p(a, b, i).print();
pos[i] = a;
}
for (int i = 1; i <= n; i++) {
while (1) {
if (stk.empty() || !meet(stk.top(), sbs[i])) {
//stk.push(sbs[i]);
break;
} else {
sb tmp = stk.top(); stk.pop();
link(tmp.pos, sbs[i].pos);
sb new_sb = make_p(place(tmp, sbs[i]), 0, tmp.pos);
pos[father(tmp.pos)] = place(tmp, sbs[i]);
did[i] = 1;
did[tmp.pos] = 1;
sbs[i] = new_sb;
}
}
stk.push(sbs[i]);
/*if (!stk.empty())
stk.top().print();
else
puts("--nil--");*/
}
for (int i = 1; i <= q; i++) {
int a;
scanf("%d", &a);
if (did[a] == 0) {
if (sav[a].second == 1)
printf("%lld\n", pos[father(a)]+t);
else
printf("%lld\n", pos[father(a)]-t);
}
else
printf("%lld\n", pos[father(a)]);
}
return 0;
}