• [CF1142E] Pink Floyd


    传送门


    题意:一个(n)个点的竞赛图,给出(m)条红色的边,其方向确定,其余边均为绿色,方向未知。你可以询问不超过(2n)次,每次询问一条绿色边的方向。要求找到一个点(x),使得(x)出发到任意一个点都有至少一条同色路径。(n ,mleq 10^5)。可能会在交互过程中动态构造图。


    考虑没有红色的边时怎么做。显然在询问过程中会形成若干棵绿色的外向树,每次询问两棵外向树的树根,将它们合并起来即可。最后剩余的点即为答案。

    回到原题,发现由于红色边的存在导致有些边无法通过询问定向,但是红色边本身可以作为连通的方式。

    将红色连通块缩点,发现此时任何没有红色入度的点均与上文中的绿色外向树等价,即,这些点满足可以任意询问两两之间的边,且这样的点只剩一个时即为答案。

    另一个值得注意的点是,合并时被删除的点可能会有若干红色出边,此时需遍历这些边,并将新的满足条件的点加入待处理点集中。

    Code:

    #include <queue>
    #include <cstdio>
    #include <cctype>
    #include <cassert>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define R register
    #define ll long long
    using namespace std;
    const int N = 110000;
    
    int hd[N], nxt[N], to[N], dfn[N], low[N], scc[N], dgrV[N], dgrS[N], stck[N], instck[N], rep[N];
    int n, m, cnt, tot, noedg = 1, top;
    queue<int> que;
    
    template <class T> inline void read(T &x) {
        x = 0;
        char ch = getchar(), w = 0;
        while (!isdigit(ch))
            w = (ch == '-'), ch = getchar();
        while (isdigit(ch))
            x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
        x = w ? -x : x;
        return;
    }
    
    inline void addEdg(int x, int y) {
        nxt[++noedg] = hd[x], hd[x] = noedg, to[noedg] = y;
        return;
    }
    
    void tarjan(int now) {
        dfn[now] = low[now] = ++cnt;
        stck[++top] = now, instck[now] = 1;
        for (R int i = hd[now]; i; i = nxt[i]) {
            int v = to[i];
            if (dfn[v]) {
                if (instck[v]) low[now] = min(low[now], dfn[v]);
            }
            else
                tarjan(v), low[now] = min(low[now], low[v]);
        }
        if (dfn[now] == low[now]) {
            rep[++tot] = now;
            while (stck[top] != now)
                instck[stck[top]] = 0, scc[stck[top--]] = tot;
            instck[now] = 0, scc[stck[top--]] = tot;
        }
        return;
    }
    
    inline void del(int x) {
        for (R int i = hd[x], v; i; i = nxt[i]) {
            if (scc[v = to[i]] != scc[x]) {
                if (!--dgrS[scc[v]]) que.push(rep[scc[v]]);
            }
            else if (!--dgrV[v] && v != rep[scc[v]]) que.push(v);
        }
        return;
    }
    
    int main() {
        int x, y;
        read(n), read(m);
        for (R int i = 1; i <= m; ++i)
            read(x), read(y), addEdg(x, y);
        for (R int i = 1; i <= n; ++i)
            if (!dfn[i])
                tarjan(i);
        for (R int i = 1; i <= n; ++i)
            for (R int j = hd[i], v; j; j = nxt[j])
                if (scc[v = to[j]] != scc[i])
                    ++dgrS[scc[v]];
                else
                    ++dgrV[v];
        for (R int i = 1; i <= tot; ++i)
            if (!dgrS[i])
                que.push(rep[i]);
        while (que.size()) {
            if (que.size() == 1) {
                cout << "! " << que.front() << endl;
                return 0;
            }
            int a = que.front(), b;
            que.pop(), b = que.front(), que.pop();
            cout << "? " << a << ' ' << b << endl;
            read(x);
            if (x) del(b), que.push(a);
            else del(a), que.push(b);
        }
        return 0;
    }
    
  • 相关阅读:
    webpack基础
    LeetCode232. 用栈实现队列做题笔记
    mysql 时间加减一个月
    leetcode 1381. 设计一个支持增量操作的栈 思路与算法
    LeetCode 141. 环形链表 做题笔记
    leetcode 707. 设计链表 做题笔记
    leetcode 876. 链表的中间结点 做题笔记
    leetcode 143. 重排链表 做题笔记
    leetcode 1365. 有多少小于当前数字的数字 做题笔记
    LeetCode1360. 日期之间隔几天 做题笔记
  • 原文地址:https://www.cnblogs.com/suwakow/p/11402331.html
Copyright © 2020-2023  润新知