Loading

[ZJOI2017]仙人掌

链接:https://www.luogu.com.cn/problemnew/solution/P3687

题目描述:给定一个无向图,求加一些便后变为仙人掌的方案数。

题解:首先,若该图不是仙人掌,直接输出零即可,判仙人掌的过程可以采用\(tarjan\),判\([dfn_{x}>dfn_{y}]+[dfn_{x}>low_{y}]<=2\)即可。

由于一条边不可以连接两个环,否则一定会连成一个仙人掌,所以要将将所有的环去掉,原问题便变为了树上问题。

考虑树型\(dp\),令\(f_{i}\)表示\(i\)的子树的匹配方案数,\(h_{i}\)表示\(i\)个点的匹配方案数,则有\(h_{i}=h_{i-1}+h_{i-2}*(i-1)\),则当\(i\)为根节点时,转移\(h_{sz_{i}-1}\),否则转移\(h_{sz_{i}}\)

#include<iostream>
#define mod 998244353
using namespace std;
struct node
{
  int v,nxt;
};
node edge[2000002];
long long sz[1000001],dfn[1000001],low[1000001],length,st[1000001],st2[1000001],g[1000001],f[1000001],n,m,S;
int rt,ct[1000001],ans[1000001],head[1000001],len,color[1000001];
bool cut[1000001],used[1000001],ins;
void add(int x,int y)
{
  edge[++len].v=y;
  edge[len].nxt=head[x];
  head[x]=len;
  return;
}
void tarjan(int x,int ed)
{
  int flag=0;
  dfn[x]=low[x]=++length;
  for (int i=head[x];i>0;i=edge[i].nxt)
    {
      if (!dfn[edge[i].v])
	{
	  tarjan(edge[i].v,i);
	  low[x]=min(low[x],low[edge[i].v]);
	  if (low[edge[i].v]<dfn[x])
	    flag++;
	  if (low[edge[i].v]>dfn[x])
	      cut[i]=cut[i^1]=1;
	}
      else if ((ed^1)!=i)
	{
	  low[x]=min(low[x],dfn[edge[i].v]);
	  if (dfn[edge[i].v]<dfn[x])
	    flag++;
	}
    }
  if (flag>=2)
    ins=1;
  return;
}
void dfs(int x)
{
  color[x]=S;
  for (int i=head[x];i>0;i=edge[i].nxt)
    {
      if (!cut[i])
        ct[edge[i].v]++;
      if (!cut[i]&&!color[edge[i].v])
        dfs(edge[i].v);
    }
  return;
}
void dp(int x)
{
  used[x]=1;
  f[x]=1;
  for (int i=head[x];i>0;i=edge[i].nxt)
      if (!used[edge[i].v]&&color[x]!=color[edge[i].v])
        {
	  dp(edge[i].v);
	  sz[x]++;
	  f[x]=f[x]*f[edge[i].v]%mod;
        }
  if (x==rt)
    f[x]=f[x]*g[sz[x]]%mod;
  if (x!=rt)
    f[x]=f[x]*g[sz[x]+1]%mod;
  return;
}
void work()
{
  int x,y;
  cin>>n>>m;
  length=ins=S=0;
  len=1;
  for (int i=1;i<=n;++i)
    {
      head[i]=0;
      ct[i]=sz[i]=dfn[i]=low[i]=color[i]=st[i]=st2[i]=used[i]=ans[i]=0;
    }
  for (int i=1;i<=2*m;++i)
    cut[i]=0;
  for (int i=1;i<=m;++i)
    {
      cin>>x>>y;
      add(x,y);
      add(y,x);
    }
  for (int i=1;i<=n;++i)
    if (!dfn[i])
        tarjan(i,0);
  for (int i=1;i<=n;++i)
    if (!color[i])
      {
	++S;
        dfs(i);
      }
  if (ins==1)
  {
    cout<<0<<endl;
    return;
  }
  g[0]=g[1]=1;
  for (int i=2;i<=n;++i)
    g[i]=((i-1)*g[i-2]+g[i-1])%mod;
  long long mul=1;
  for (int i=1;i<=n;++i)
    if (!used[i])
      {
	rt=i;
	dp(i);
	mul=(mul*f[i])%mod;
       }
  cout<<mul<<endl;
  return;
}
int main()
{
  int T;
  cin>>T;
  while (T--)
    work();
  return 0;
}
posted @ 2022-12-14 21:25  zhouhuanyi  阅读(34)  评论(0)    收藏  举报