[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;
}
本文来自博客园,作者:zhouhuanyi,转载请注明原文链接:https://www.cnblogs.com/zhouhuanyi/p/16983615.html

浙公网安备 33010602011771号