zoukankan      html  css  js  c++  java
  • ZJOI 2017 仙人掌

    题链

    SOL: 一道很奇怪的计数题。

     我们先考虑树的做法:

    用h[i]表示有i个带匹配的子树,它们之间匹配的方案数

    h[i]=h[i-1]+(i-1)*h[i-2]

    • 如果i子树不与其他子树相连,那么方案就是h[i−1]
    • 如果与其他子树连接,那么有(i−1)中选择方式,而当选择一个子树以后,有两个子树不能再连接,那么方案就是(i−1)∗h[i−2]

     

      f[i]表示做完以i为根的子树,且没有路径可以向上扩展。 
      g[i]表示做完以i为根的子树,且有路径可以向上扩展。 

     f[x]=Πg[son]×h[num]

     g[x]=f[x]+Πg[son]×h[num1]×num

    我们考虑仙人掌,我们发现环对答案没有贡献,将其删掉就好了。

    那么变成了一片森林,就可以做了。

    #include<bits/stdc++.h>
    #define N 500107
    #define M N<<2|1
    #define mo 998244353
    #define LL long long
    using namespace std;
    LL h[N]; int n,T,a,b,m;
    void pre() {
        h[0]=1;
        for (int i=1;i<N;i++) 
          h[i]=(h[i-1]+(i>1?h[i-2]*(i-1):0))%mo;
    }
    #define sight(c) ('0'<=c&&c<='9')
    inline void read(int &x){
        static char c;
        for (c=getchar();!sight(c);c=getchar());
        for (x=0;sight(c);c=getchar())x=x*10+c-48;
    }
    void write(int x){if (x<10) {putchar('0'+x); return;} write(x/10); putchar('0'+x%10);}
    inline void writeln(int x){ if (x<0) putchar('-'),x*=-1; write(x); putchar('
    '); }
    inline void writel(int x){ if (x<0) putchar('-'),x*=-1; write(x); putchar(' '); }
    struct Node{
        #define eho(x) for(int i=head[x];i;i=net[i])
        #define v fall[i]
        LL ans,f[N],g[N];
        int s[N],top,tim,tot,vis[N],low[N],dfn[N],head[N],net[M],fall[M],catus,col[N];
        void clear(int n) { n=min(sizeof vis,(3+n)*(sizeof top));
          ans=1; memset(vis,0,n); top=0; tim=0;
          tot=1; memset(head,0,n); memset(col,0,n);
          memset(dfn,0,n),memset(low,0,n); catus=0;
        }
        void init() {
          ans=1; memset(vis,0,sizeof vis); top=0; tim=0;
          tot=1; memset(head,0,sizeof head); memset(col,0,sizeof col);
          memset(dfn,0,sizeof dfn),memset(low,0,sizeof low); catus=0;
        }
        inline void add(int x,int y){
            fall[++tot]=y; net[tot]=head[x]; head[x]=tot;
        }
        inline void adds(int x,int y) {
            add(x,y); add(y,x);
        }
        void Tarjan(int x,int fa){
            dfn[x]=low[x]=++tim;
            s[++top]=x;
            bool flag=0;
            eho(x) if (v!=fa) {
                if (!dfn[v])  { Tarjan(v,x); 
                   low[x]=min(low[x],low[v]);
                   if (low[v]<dfn[x]) {
                       if (flag) {catus=1;return;}
                       flag|=1;
                   }
                } else {
                    low[x]=min(low[x],dfn[v]);
                    if (dfn[v]<dfn[x]) {
                      if (flag) {catus=1;return;}
                         flag|=1;    
                    }
                }
            }
            if (dfn[x]==low[x]) 
                do col[s[top--]]=x; while (s[top+1]!=x);
        }
        void dfs(int x,int fa){
            vis[x]=1;
            f[x]=1; g[x]=0;
            int num=0;
            eho(x) {
                if (col[x]==col[v]||fa==v) continue;
                dfs(v,x);
                f[x]=f[x]*g[v]%mo;
                num++;
            }
            g[x]=f[x]*h[num]%mo+f[x]*h[num-1]%mo*num%mo;
            f[x]=f[x]*h[num]%mo;
        }
        inline LL work() {
            Tarjan(1,0);
            if (catus) return 0;
            for (int i=1;i<=n;i++) if (!vis[i]) {
                dfs(i,0); ans=ans*f[i]%mo;
            } return ans;
        }
    }G;int NN;
    signed main () {
        freopen("a.in","r",stdin);
        freopen("a.out","w",stdout);
        read(T); pre(); G.init();
        while (T--) {
            read(n); read(m); //NN=max(n,m);
            while (m--) read(a),read(b),G.adds(a,b);
            writeln(G.work());
            G.clear(n);
        } 
        return 0;
    }
  • 相关阅读:
    初识Ubuntu 18.04(更换系统头像,截图,sy)
    HDU
    HDU
    进度1
    开课博客
    返回一个整数数组中最大子数组的和
    开学第一节课测试感受
    网页记账本开发四(基本完成)
    网页记账本开发三
    网页记账本开发二
  • 原文地址:https://www.cnblogs.com/rrsb/p/8538965.html
Copyright © 2011-2022 走看看