标签: CF

  • CF1521C Nastia and a Hidden Permutation

    真是个沙比提,比赛的时候也想出来了,但是因为太迷糊了没写完

    首先我们先考虑如何确定下最大值所在的位置。

    执行询问$max(min(n-1,p_i),min(n,p_{i+1}))$,这个询问实际上等价于$max(min(n-1,p_i),p_{i+1})$。

    • 如果结果是n,那么说明$p_{i+1}=n$(很显然,max里第一项不会超过n-1)。
    • 如果结果比n-1要小,那么说明$p_i$和$p_{i+1}$都不是n。
    • 如果结果是n-1,那么就要讨论下了。这个时候n可能出现在这两个数里,也可能没出现。

    我们再构造另一个询问:$min(max(n-1,p_i),max(n,p_{i+1}))$,显然这个询问等价于$max(n-1,p_i)$,所以我们可以利用这个询问来检查$p_i$的值是不是n。

    在找到最大值的位置,设它为$maxpos$,那么我们对于每一个$i\neq maxpos$,构造询问$min(max(1,p_i),max(2,p_{maxpos}))$,这个询问等价于$p_i$,然后就可以求出来每个位置的值了。

    #include <algorithm>
    #include <iostream>
    using std::cin;
    using std::cout;
    using std::endl;
    using int_t = long long;
    int_t result[int_t(1e5)];
    int main() {
        std::ios::sync_with_stdio(false);
        int_t T;
        cin >> T;
        while (T--) {
            int_t n;
            cin >> n;
            int_t maxpos = -1;
            const auto check = [&](int_t i, int_t j) {
                cout << "? 1 " << i << " " << j << " " << n - 1 << endl;
                int_t x;
                cin >> x;
                if (x == n) {
                    maxpos = j;
                    return;
                }
                if (x < n - 1)
                    return;
                // x==n-1
                for (int_t _ = 1; _ <= 2; _++) {
                    cout << "? 2 " << i << " " << j << " "<< n - 1 << endl;
                    cin >> x;
                    if (x == n) {
                        maxpos = i;
                    }
                    std::swap(i, j);
                }
            };
            for (int_t i = 1; i <= n && i + 1 <= n && maxpos == -1; i += 2) {
                int_t j = i + 1;
    
                check(i, j);
            }
            if (maxpos == -1) {
                maxpos = n;
            }
            result[maxpos] = n;
            for (int_t i = 1; i <= n; i++) {
                if (i == maxpos)
                    continue;
                cout << "? 2 " << i << " " << maxpos << " 1" << endl;
                ;
                int_t x;
                cin >> x;
                result[i] = x;
            }
            cout << "! ";
            for (int_t i = 1; i <= n; i++) {
                cout << result[i] << " ";
            }
            cout << endl;
        }
        return 0;
    }
    

     

  • CF19E Fairy

    题意:

    删掉若干条边使原图成为二分图的方案数。

    题解:

    考虑DFS树。

    答案为所有奇环的交减掉偶环的并。

    这里的环指的是由一条非树边和若干条树边组成的环。

    如果一个奇环和偶环在树边上有交集,那么删掉交集部分后会形成新的奇环,而删掉奇环的非树边则会破坏奇环。

     

    首先考虑只有一个奇环的情况。

    首先奇环的非树边显然是可以删掉的,删掉后破坏了奇环。

    其次,奇环的树边,只要不被另一个偶环覆盖,那么也可以删(如果被覆盖了,删掉后会拼成一个新的奇环)

    考虑由多个奇环的情况。

    答案是所有奇环的公共部分。

    直接在树上打标记即可。

    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <cmath>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t LARGE = 10000;
    
    struct State
    {
        int_t depth;
        int_t vertex;
        bool operator<(const State &x) const
        {
            return depth < x.depth;
        }
        State(int_t vertex = -1, int_t depth = 0)
        {
            this->depth = depth;
            this->vertex = vertex;
        }
    } seq[20][LARGE * 3 + 1];
    struct Edge
    {
        int_t to;
        int_t id;
        int_t from = -1;
    };
    int_t count = 0;
    int_t first[LARGE + 1];
    int_t depth[LARGE + 1];
    std::vector<Edge> graph[LARGE + 1];
    std::vector<Edge> nonetree[LARGE + 1];
    std::vector<int_t> stage[LARGE + 1];
    Edge inEdge[LARGE + 1];
    bool visited[LARGE + 1];
    int_t mark[LARGE + 1];
    int_t parent[LARGE + 1];
    int_t n, m;
    void DFS0(int_t vertex, int_t from = -1, int_t depth = 1)
    {
        if (visited[vertex])
            return;
        // cout << "visiting " << vertex << endl;
        ::parent[vertex] = from;
        stage[depth].push_back(vertex);
        ::depth[vertex] = depth;
        visited[vertex] = true;
        seq[0][++count] = State(vertex, depth);
        if (first[vertex] == 0)
            first[vertex] = count;
        for (auto &edge : graph[vertex])
        {
            if (visited[edge.to] && edge.to != from && ::depth[edge.to] > ::depth[vertex])
            {
                nonetree[vertex].push_back(edge);
            }
            else if (visited[edge.to] == false)
            {
                inEdge[edge.to] = edge;
                DFS0(edge.to, vertex, depth + 1);
                seq[0][++count] = State(vertex, depth);
                if (first[vertex] == 0)
                    first[vertex] = count;
            }
        }
    }
    void init()
    {
        for (int_t i = 1; (1 << i) <= count; i++)
        {
            for (int_t j = 1; j + (1 << i) - 1 <= count; j++)
            {
                seq[i][j] = std::min(seq[i - 1][j], seq[i - 1][j + (1 << (i - 1))]);
            }
        }
    }
    int_t getLCA(int_t v1, int_t v2)
    {
        v1 = first[v1];
        v2 = first[v2];
        if (v1 > v2)
            std::swap(v1, v2);
        int_t i = std::log2(v2 - v1 + 1);
        return std::min(seq[i][v1], seq[i][v2 - (1 << i) + 1]).vertex;
    }
    int main()
    {
        cin >> n >> m;
        for (int_t i = 1; i <= m; i++)
        {
            int_t from, to;
            cin >> from >> to;
            graph[from].push_back(Edge{to, i});
            graph[to].push_back(Edge{from, i});
        }
        for (int_t i = 1; i <= n; i++)
        {
            if (visited[i] == false)
            {
                DFS0(i);
            }
        }
        init();
        int_t count = 0;
        std::vector<Edge> evens;
        for (int_t i = 1; i <= n; i++)
        {
            for (auto &edge : nonetree[i])
            {
                int_t dis = depth[i] + depth[edge.to] - 2 * depth[getLCA(edge.to, i)] + 1;
                int_t mark;
                //偶环
                if (dis % 2 == 0)
                {
                    mark = -1;
                }
                else
                { //奇环
                    mark = 1;
                    count++;
                    evens.push_back(Edge{i, edge.id, edge.to});
                }
                ::mark[i] += mark;
                ::mark[edge.to] += mark;
                ::mark[getLCA(edge.to, i)] -= 2 * mark;
            }
        }
        for (int_t i = n; i >= 2; i--)
        {
            for (int_t vtx : stage[i])
            {
                mark[parent[vtx]] += mark[vtx];
            }
        }
        std::vector<int_t> result;
        if (count == 0)
        {
            for (int_t i = 1; i <= m; i++)
            {
                result.push_back(i);
            }
        }
        else if (count == 1)
        {
            int_t v1 = evens[0].from;
            int_t v2 = evens[0].to;
            int_t lca = getLCA(v1, v2);
            result.push_back(evens[0].id);
            while (v1 != lca)
            {
                if (mark[v1] == 1)
                {
                    result.push_back(inEdge[v1].id);
                }
                v1 = parent[v1];
            }
            while (v2 != lca)
            {
                if (mark[v2] == 1)
                {
                    result.push_back(inEdge[v2].id);
                }
                v2 = parent[v2];
            }
        }
        else if (count > 1)
        {
            for (int_t i = 1; i <= n; i++)
            {
                if (parent[i] != -1 && mark[i] == evens.size())
                {
                    result.push_back(inEdge[i].id);
                }
            }
        }
        std::sort(result.begin(), result.end());
        cout << result.size() << endl;
        for (int_t x : result)
            cout << x << " ";
        cout << endl;
        return 0;
    }

     

  • CF407B Long Path

    https://www.luogu.org/problemnew/show/CF407B

    递推。

    设$f_i$表示从第$i-1$个房间到第$i$个房间所需要的步数。

    经过观察可得,到达第$i$个房间时,前$i-1$个房间一定经过了偶数次。

    那么$f_i$等于1(从$i-1$走到$i$)+1(到了$i$后走到$p_i$)+$\sum _{j=p_i}^{i-1}$(从$p_i$走回i)

    最后把所有的$p_i$加起来即可

    #include <iostream>
    
    using std::cin;
    using std::cout;
    using std::endl;
    using int_t = long long int;
    
    const int_t mod = 1000000007;
    const int_t LARGE = 1000;
    
    int_t seq[LARGE + 1];
    int_t dp[LARGE + 1];
    int_t n;
    int main()
    {
        cin >> n;
        for (int_t i = 1; i <= n; i++)
        {
            cin >> seq[i];
        }
        dp[0] = 1;
        for (int_t i = 1; i <= n; i++)
        {
            dp[i] += 1 + 1;
            for (int_t j = seq[i]; j < i; j++)
            {
                dp[i] += dp[j];
                dp[i] %= mod;
            }
            dp[i] %= mod;
        }
        int_t result = 0;
        for (int_t i = 1; i <= n; i++)
        {
            result += dp[i];
            result %= mod;
        }
        cout << result << endl;
        return 0;
    }