zoukankan      html  css  js  c++  java
  • CF1254E Send Tree to Charlie

    题意

    讲不太清楚,看英文吧
    cf

    做法

    在正式开始之前,我们先来玩一玩性质

    首先考虑全\(0\)的情况,即本质不同的方案数
    性质1:方案数并不为(n-1)!,即方案与结果不为双射

    考虑一条边将树分为两个联通块,之间是互不影响的

    结论1:方案数为\(\prod\limits (deg_i!)\)

    考虑节点\(u\)的父节点与其父亲这条边已经选过
    对于每种除\(u\)的儿子外的边固定时,\(u\)的儿子边的全排列决定了其对子树值的影响
    ps:实际上这是无根树,但并不影响映射关系

    性质2:若\(n\ge 2\)\(a_i\neq i\)

    结论2:若知晓所有限制,\(\sum\limits dis(i,a_i)=2(n-1)\)

    具体考虑\(a_v=u\)的限制,有以下结论

    • 存在相对顺序边集\((u,t_1)(t_1,t_2)...(t_k,v)\)
    • \((u,t_1)\)\(u\)的第一条边
    • \((t_k,v)\)\(v\)的最后一条边
    • \((t_{i-1},t_{i})(t_i,t_{i+1})\)\(t_i\)严格顺序的边

    我们可以依此建立顺序,合法的方案,对于某个点,有以下限制

    • 不可破坏简单顺序
    • 不可生成环,即为一种非自然的复杂顺序
    • 若有严格顺序从第一条边到最后一条边,中间不可漏边

    方案数,对于无限制的边随便选择顺序

    题外话

    这题很妙,虽然容易理解,但需要想满条件还是需要时间的,由于没出现什么算法,强行归到拓扑排序上了

    code(std)

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int mod = 1e9 + 7;
    const int maxn = 500005;
    
    int n;
    vector <int> adj[maxn];
    int a[maxn];
    int dad[maxn];
    int h[maxn];
    vector <pair <int, int> > conditions[maxn];
    int nxt[maxn], prv[maxn], seen[maxn];
    
    void no(int ncase) {
        cerr << ncase << endl;
        cout << 0 << endl;
        exit(0);
    }
    
    void dfs(int u) {
        for (auto v: adj[u]) if (v != dad[u]) {
            dad[v] = u;
            h[v] = h[u] + 1;
            dfs(v);
        }
    }
    
    int cnt; ///total distance, must not be more than 2n-2
    
    void go(int u, int v) {
        if (u == v) no(0);
        vector <int> from_u, to_v;
        ///naive LCA works here as long as we exit upon finding a conflict
        from_u.push_back(n + 1); ///first edge (fake)
        to_v.push_back(n + 2); ///last edge (fake)
        while (h[u] > h[v]) {
            from_u.push_back(u);
            u = dad[u];
        }
        while (h[v] > h[u]) {
            to_v.push_back(v);
            v = dad[v];
        }
        while (u != v) {
            from_u.push_back(u);
            u = dad[u];
            to_v.push_back(v);
            v = dad[v];
        }
        from_u.push_back(u);
        from_u.insert(from_u.end(), to_v.rbegin(), to_v.rend());
        for (int i = 1; i + 1 < from_u.size(); ++i)
            conditions[from_u[i]].push_back({from_u[i-1], from_u[i+1]});
        cnt += from_u.size() - 3;
        if (cnt > 2 * n - 2) no(0); ///important break
    }
    
    int main(void) {
        ios_base::sync_with_stdio(0);
        cin.tie(NULL);
        cin >> n;
        for (int i = 1; i < n; ++i) {
            int u, v;
            cin >> u >> v;
            adj[u].push_back(v);
            adj[v].push_back(u);
        }
        for (int i = 1; i <= n; ++i) {
            adj[i].push_back(n + 1); ///first edge (fake)
            adj[i].push_back(n + 2); ///last edge (fake)
        }
        for (int i = 1; i <= n; ++i) cin >> a[i];
    
        if (n == 1) {
            cout << 1 << endl;
            exit(0);
        }
    
        dfs(1);
        for (int i = 1; i <= n; ++i) if (a[i] != 0) go(i, a[i]);
    
        ///answer 0 if:
        ///1. there are 2 or more incoming/outgoing
        ///conditions to/from an edge, or
        ///2. there is a cycle, or
        ///3. first (n+1) is connected to last (n+2),
        ///but does not contain all other edges.
        int ans = 1;
        for (int i = 1; i <= n; ++i) {
            ///check case 1
            for (auto edge: conditions[i]) {
                int u = edge.first, v = edge.second;
                if (nxt[u] && nxt[u] != v) no(1);
                if (prv[v] && prv[v] != u) no(1);
                nxt[u] = v;
                prv[v] = u;
            }
            ///check case 2
            for (auto u: adj[i]) if (!seen[u]) {
                seen[u] = 1;
                int cur = nxt[u];
                while (cur) {
                    if (cur == u) no(2);
                    if (seen[cur]) break;
                    seen[cur] = 1;
                    cur = nxt[cur];
                }
            }
            ///check case 3
            if (nxt[n+1]) {
                int cur = n + 1, all = 1;
                while (cur) {
                    if (cur == n + 2) break;
                    ++all;
                    cur = nxt[cur];
                }
                if (cur == n + 2 && all < adj[i].size()) no(3);
            }
            ///all good - for now
            int free = 0;
            for (auto u: adj[i]) if (u <= n && !prv[u]) ///fake edges doesn't count
                ++free;
            if (prv[n+2]) --free; ///connected to last edge => not free
            for (int j = 1; j <= free; ++j) ans = 1ll * ans * j % mod;
            ///reset
            for (auto u: adj[i]) nxt[u] = prv[u] = seen[u] = 0;
        }
    
        ///no conflicts
        cout << ans << endl;
        return 0;
    }
    
  • 相关阅读:
    javaScript基本语法函数
    JavaScript内置对象 以及和 内置对象相关的语法
    SCRIPT5007: 属性“test”的值为 null、未定义或不是 Function 对象
    Dom4j的使用(全而好的文章)
    JavaScript的对话框
    Ajax重构
    java中4种操作(DOM,SAX,JDOM,DOM4J)xml方式详解与比较
    Dom4j的基本使用
    JavaScript事件处理(重要)
    母函数(Generating function)详解
  • 原文地址:https://www.cnblogs.com/Grice/p/12292391.html
Copyright © 2011-2022 走看看