luogu解题报告:P3407散步【栈】【奇技淫巧】

题目见https://www.luogu.org/problem/show?pid=3407

分析

定理一:如果从西向东顺次有A,B,C三人,再A与C发生相遇(如果会)之前,必然会有B先和A或B先和C相遇。这是显然的。换句话说,不会出现跨过某人发生相遇。这是一个明显的操作具有顺序性的提示。

定理二:把若干个运动状态相同的人看作一“组”,则每一组有且只有三种情况:静止,向西运动,向东运动。

定理三:对于任意相邻的两个组,他们相遇当且仅当他们相向而行或一个静止一个朝向另一个运动,且运动时间少于T。如果相向而行,相遇点在两组正中;否则在静止的那一组。

至此可以有一个大概的思路:维护一种元素A,包含其位置,运动状态,人。维护一个此元素栈,所有人开始视为独立的元素,自西向东按顺序入栈,如果一个元素A等待入栈:

  1. 栈为空,则入栈
  2. 栈顶元素与A在给定时间内不相遇,入栈
  3. 栈顶元素与A在给定时间内相遇,将栈顶元素出栈,并在两者中点新建一个组,人为原先两组的人的并,标记为静止组。将栈顶元素和A中所有的人的位置更改为新位置。

由于定理1,该方法必然会使所有相遇“顺序”发生,因而不会出现抢人的问题。

人处于哪一组可以用并查集实现,注意对于无法与其他元素相遇的元素,输出时要特别处理。复杂度通过摊还分析,容易得知为O(nα(n))

示例代码

#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;
}
posted @ 2016-10-23 19:08  ljt12138  阅读(131)  评论(0编辑  收藏  举报