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

    题面

    https://www.luogu.org/problem/P3687

    题解

    如果原图不是仙人掌(在这里,我们认为一棵树也是一个仙人掌),则无解,输出$0$。

    如果原图是一个仙人掌,加的边是不能跨越仙人掌上的环边的(若跨越,则仙人掌上的环边被$2$个环所有,不是仙人掌),所以我们直接把环边断掉,变成一个森林,直接统计每棵树的答案,最后乘法原理乘起来就可以了。

    首先,很自然的想到,令$F[i]$表示$i$节点的子树及它的父边形成一个仙人掌的方案数(父边可选可不选),这个状态不同寻常,一般我们的状态都是不带父边的。但是这道题不一样,稍微分析一下就可以得到这个状态。(这个应该不是难点吧)

    如果是按寻常的方法转移,这个状态是有问题的,因为要考虑儿子之间可能还有边,我的解决方法是分父边一定被覆盖和父边一定没有被覆盖两个状态讨论,这样只能做到$O(sum{deg^2})$。

    题解很神奇,先预处理出一个数组$h[i]$,表示有$i$个点,他们之间互相匹配的方案数,显然,分$i$不配对还是和别人配对,有$h[i]=h[i-1]+(i-1)h[i-2]$(吐槽:我觉得这个数组只是处理出了特殊情况,如果一个点$x$,对于它的每个儿子$y$都有$f[y]=1$,是可以这么转移的,但是$f[y]$可以是任意值,就假了啊),然后是一个神奇的结论(特殊情况得到一般结论?)有$$f[x]=prod_{(x,y) in E}{f[y]} imes h[deg[x]]$$,看了题解之后,我的脑子里也预想过这个结论,但是不会证明(大概是乘法原理对应每一种情况吧),不管了,既然结论这么好记,我就记下了行吧。

    问题在于,我无法给出一种构造方法,使得不知道$y$的父边有没有选上的情况和$y'$配对,有大佬知道吗$qwq$?

    upd2019.8.30:当$y$的父边没有被选上时,链的一个端点就是$y$,当$y$的父边已被选上时,这条链一定向下指向一个$y$的子树中(不包含$y$)的点,以它作为链的端点,并把原来的链删去。对于$x$的父边,如果有对象,就拿$fa[x]$连,如果没有,就空着。
    至于这样为什么是不重不漏的,让我再想想。

    写这道题的时候调试了很长时间,最后发现只是一个后来加的$s.pop()$忘了把$ins[y]$赋值为$0$,这种粗心的毛病已经不是第一次了,每次都会因此浪费很长时间,要是在考试时遇到,估计心态就崩了吧。

    update 2020.6.09

    $h_n$ 即为互不相同的 $n$ 阶对合置换的数量。

    给出任意一个对合置换 $sigma$,我们可以构造出一种合法的方案。

    若 $(x,x) in sigma$,我们生成一个 $(x,fa_x)$ 的链,在后面用。

    若 $(c_i,c_j)in sigma$,我们直接把两条链的头接起来。一定合法(链长 $ge 2$),并且不会对后来的决策造成影响。

    若 $(c_i,x)in sigma$,我们直接把 $c_i$ 链的头和 $(x,fa_x)$ 边接起来,显然,这条链一定合法,会对后面造成影响。

    若 $(c_i,c_i)in sigma$,我们看看 $c_i$ 链的长度,如果是 $1$,就抛弃在这里,这条边空着,如果 $ge 2$,就连起来,也不会对后来的决策影响了。

    #include<stack>
    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define N 500500
    #define M 1000500
    #define ri register int
    #define mod 998244353
    #define LL long long 
    using namespace std;
    
    inline int read() {
      int ret=0; char ch=getchar();
      while (ch<'0' || ch>'9') ch=getchar();
      while (ch>='0' && ch<='9') ret*=10,ret+=(ch-'0'),ch=getchar();
      return ret;
    }
    
    int f[N],g[N],cloock,fl=0;
    int dfn[N],low[N];
    int to[2*M],plc[N];
    vector<int> ed[N],tr[N];
    bool fib[2*M],ins[N];
    stack<int> s;
    
    void del(int e) {fib[e]=fib[1^e]=1; }
    
    void tarjan(int x) {
      dfn[x]=low[x]=++cloock;
      s.push(x); plc[x]=s.size(); ins[x]=1;
      for (ri i=0;i<ed[x].size();i++) {
        int e=ed[x][i];
        int y=to[e];
        if (!dfn[y]) {
          tarjan(y);
          low[x]=min(low[x],low[y]);
          if (low[y]>=dfn[x]) {
            if (s.top()==y) {
              s.pop(); ins[y]=0;
              continue;
            }
            del(e);
            int pre=x,tail=s.top();
            while (1) {
              int t=s.top(); s.pop(); ins[t]=0;
              int suc=(t==y)?(x):(s.top());
              for (ri j=0;j<ed[t].size();j++) {
                int r=to[ed[t][j]];
                if (ins[r] && (plc[r]>=plc[y] || r==x)) {
                  if (r!=suc && r!=pre && !(t==tail && r==x)) { if (fl==0) puts("0"); fl=1; }
                }
                if (r==pre) del(ed[t][j]);
              }
              pre=t; if (t==y) break;
            }
          }
        }
        else {
          low[x]=min(low[x],dfn[y]);
        }
      }
    }
    
    int dp(int x,int ff) {
      f[x]=1;
      for (ri i=0;i<tr[x].size();i++) {
        int y=tr[x][i]; if (y==ff) continue;
        dp(y,x);
        f[x]=(f[x]*1LL*f[y])%mod;
      }
      f[x]=(f[x]*1LL*g[tr[x].size()])%mod;
      return f[x];
    }
    
    int main() {
      //freopen("4.in","r",stdin);
      //freopen("4.out","w",stdout);
      g[0]=g[1]=1;
      for (ri i=2;i<N;i++) g[i]=(g[i-1]+(g[i-2]*1LL*(i-1))%mod)%mod;
      int T=read();
      while (T--) {
        cloock=0;
        int n=read(),m=read();
        for (ri i=1;i<=n;i++) ed[i].clear(),tr[i].clear();
        for (ri i=1;i<=n;i++) ins[i]=0;
        while (!s.empty()) ins[s.top()]=0,s.pop();
        for (ri i=1;i<=n;i++) dfn[i]=low[i]=0;
        int cntb=-1;
        for (ri i=1;i<=m;i++) {
          int u=read(),v=read();
          ed[u].push_back(++cntb); to[cntb]=v; fib[cntb]=0;
          ed[v].push_back(++cntb); to[cntb]=u; fib[cntb]=0;
        }
        fl=0;
        for (ri i=1;i<=n;i++) if (!dfn[i]) {
          tarjan(i);
          while (!s.empty()) ins[s.top()]=0,s.pop();
        }
        if (fl) continue;
        for (ri i=0;i<=cntb;i++) if (!fib[i]) {
          tr[to[i^1]].push_back(to[i]);
        }
        for (ri i=1;i<=n;i++) f[i]=0;
        LL ans=1;
        for (ri i=1;i<=n;i++) if (!f[i]) ans*=dp(i,-1),ans%=mod;
        printf("%d
    ",ans);
      }
      return 0;
    }
  • 相关阅读:
    MSBuild最佳实践
    Javascript:阻止浏览器默认右键事件,并显示定制内容
    zeptoJS:如何像jQuery一样,让滚动变得优雅?
    Javascript:DOM表格操作
    Javascript:getElementsByClassName
    Javascript:DOM动态创建元素实例应用
    Javascript:倒计时
    Javascript:sort()方法快速实现对数组排序
    探究css !important的应用之道
    Javascript:splice()方法实现对数组元素的插入、删除、替换及去重
  • 原文地址:https://www.cnblogs.com/shxnb666/p/11429135.html
Copyright © 2011-2022 走看看