zoukankan      html  css  js  c++  java
  • 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 --
    ", 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
    ", pos[father(a)]+t);
                else
                    printf("%lld
    ", pos[father(a)]-t);
            }
            else
                printf("%lld
    ", pos[father(a)]);
        }
        return 0;
    }
  • 相关阅读:
    开发人员需要熟知的常用Linux命令之四:Scp Leone
    linux下WordPress文件夹权限设置 Leone
    计算机经典教材(计算机牛人的必由之路)
    wampserver打开localhost显示 已经找到网站,正在等待回应。
    ffmpeg常用数据结构4
    ffmpeg常用数据结构3
    ffmpeg程序流程图
    ffmpeg常用数据结构2
    ffmpeg常用数据结构一
    热门wordpress插件 TOP 10
  • 原文地址:https://www.cnblogs.com/ljt12138/p/6684367.html
Copyright © 2011-2022 走看看