zoukankan      html  css  js  c++  java
  • 【题解】ZJOI2017仙人掌

      感觉这题很厉害啊,虽然想了一天多但还是失败了……(;д;)

      这题首先注意到给定图中如果存在环其实对于答案是没有影响的。然后关键之处就在于两个 (dp) 数组,其中 (f[u]) 表示以 (u) 为根的子树中能构成仙人掌的方案数, 而 ( g[x] ) 则表示 (x) 个节点之间两两相互搭配(可以不搭配)的总方案数。转移则为:

     (f[u] = prod f[v] * g[tot + [u != root]])

      其中 (v) 为 (u) 的儿子节点,而 (tot) 表示 (u) 的总儿子个数。为什么这样做是对的呢?我也感到非常的困惑。之前自己在思考的时候其实有一个问题一直难住我:一个节点的儿子之间可以相互连边,这怎样处理?但此时我们将这些方案巧妙地连接在了一起。我们可以默认为求出来的 (f[u]) 中的方案数均为有一条边连向外界的方案。当这个方案匹配到另一子树的一种方案上的时候,表示这两条连向外界的边连接在了一起。若有没有匹配的,说明这条边没有连出去或连向根节点(若连向根节点且该点为儿子节点则说明没有连出去),但一样是合法的。

      非常的厉害啊~其实感觉自己现在各种知识储备都还算可以了,但就是不够大胆,不能勇敢的提出一些想法和设想。一定要努力放开自己的思维,先猜测,再证明~

    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 1000000
    #define mod 998244353
    #define int long long
    int n, m, dep[maxn], g[maxn];
    int timer, dfn[maxn], f[maxn];
    int fa[maxn], mark[maxn], tot;
    int cnt, ans;
    
    struct edge
    {
        int cnp, head[maxn], to[maxn], last[maxn];
        edge() { cnp = 1; }
        void add(int u, int v)
        {
            to[cnp] = v, last[cnp] = head[u], head[u] = cnp ++;
            to[cnp] = u, last[cnp] = head[v], head[v] = cnp ++;
        }
    }E1;
    
    struct node
    {
        int id, dep;
    }a[maxn];
    
    bool cmp(node a, node b) { return a.dep < b.dep; }
    
    int read()
    {
        int x = 0, k = 1;
        char c;
        c = getchar();
        while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * k;
    }
    
    void pre()
    {
        g[0] = g[1] = 1;
        for(int i = 2; i < maxn; ++ i) g[i] = (g[i - 1] + (i - 1)*g[i - 2]) % mod;
        return;
    }
    
    void Tarjan(int u)
    {
        dfn[u] = ++ timer;
        for(int i = E1.head[u]; i; i = E1.last[i])
        {
            int v = E1.to[i]; if(dfn[v]) continue;
            fa[v] = u; dep[v] = dep[u] + 1; Tarjan(v);
        }
        return;
    }
    
    void dfs(int u, int rt)
    {
        mark[u] = -1; f[u] = 1; int tot = 0;
        for(int i = E1.head[u]; i; i = E1.last[i])
        {
            int v = E1.to[i]; if(v == fa[u] || mark[v] != 1) continue;
            tot ++; dfs(v, 0); f[u] = f[u] * f[v] % mod;
        }
        if(!rt) f[u] = f[u] * g[tot + 1] % mod;
        else f[u] = f[u] * g[tot] % mod;
        return;
    }
    
    void Work()
    {
        n = read(), m = read(); E1.cnp = 2;
        for(int i = 1; i <= n; i ++) mark[i] = fa[i] = dep[i] = dfn[i] = E1.head[i] = 0;
        for(int i = 1; i <= m; i ++)
        {
            int u = read(), v = read();
            E1.add(u, v);
        }
        dep[1] = 1; Tarjan(1);
        for(int i = 1; i <= m; i ++)
        {
            int u = E1.to[i << 1], v = E1.to[i << 1 | 1];
            if(dfn[u] < dfn[v]) swap(u, v);
            while(u != v) 
            {
                if(mark[u] == 2) { printf("0
    "); return; }
                mark[u] ++; u = fa[u];
            }
        }
        for(int i = 1; i <= n; i ++) a[i].id = i, a[i].dep = dep[i];
        sort(a + 1, a + n + 1, cmp); ans = 1;
        for(int i = 1; i <= n; i ++)
        {
            int x = a[i].id; if(mark[x] == -1) continue;
            dfs(x, 1); ans = ans * f[x] % mod;
        }
        printf("%lld
    ", ans); return;
    }
    
    signed main()
    {
        pre(); int T = read();
        while(T --) Work();
        return 0;
    }

      

  • 相关阅读:
    hbase过滤器(1)
    公司jar包提交到集群的方法
    hbase Hfile处理原因
    oracle pl/sql远程连接过程
    mapreduce join操作
    HTML不熟悉方法总结
    Ajax详解
    getElementById和querySelector区别
    Session
    ES6模块化
  • 原文地址:https://www.cnblogs.com/twilight-sx/p/9230703.html
Copyright © 2011-2022 走看看