Description
Output
Sample Input
样例 1 输入
3 3
1 2
2 3
3 1
Sample Output
样例 1 输出
2
首先有贡献的排列必须满足每个点恰好属于一个环(具体不太会证明)
一种分配方式的贡献为 2a ∗ (−1)b,a 为长度 > 2 的环的数量(方向有 2 种)b 为长度为偶数的环的数量(交换行列式的两列符号改变);
于是我们可以用圆方树dp来求解分配价值
大概就是考虑当前点是否被分配的子树价值
#include
#define ll long long
using namespace std;
const int N=2e5+77,M=N*2,mod=998244353;
int n,m,du[N],cnt,e[M],nx[M],ls[N],Cnt,E[M],next[M],Ls[M];
void add(int x,int y)
{
cnt++; e[cnt]=y; nx[cnt]=ls[x]; ls[x]=cnt;
cnt++; e[cnt]=x; nx[cnt]=ls[y]; ls[y]=cnt;
}
void Add(int x,int y)
{
Cnt++; E[Cnt]=y; next[Cnt]=Ls[x]; Ls[x]=Cnt;
Cnt++; E[Cnt]=x; next[Cnt]=Ls[y]; Ls[y]=Cnt;
}
int tot,dfn[N],low[N],d[N],bz[M],nn;
void tarjan(int x)
{
dfn[x]=low[x]=++tot,d[++d[0]]=x;
for(int i=ls[x]; i; i=nx[i]) if(!bz[i])
{
bz[i]=bz[i^1]=1;
if(!dfn[e[i]])
{
tarjan(e[i]),low[x]=min(low[x],low[e[i]]);
if(low[e[i]]>=dfn[x])
{
nn++;
while(1)
{
Add(nn,d[d[0]]),d[0]--,du[nn]++;
if(d[d[0]+1]==e[i]) break;
}
Add(nn,x),du[nn]++;
}
} else low[x]=min(low[x],dfn[e[i]]);
}
}
ll f[N][2],g[N][2];
void solve(int x,int p)
{
if(x<=n)
{
f[x][0]=1;
for(int i=Ls[x]; i; i=next[i]) if(E[i]!=p)
{
solve(E[i],x);
f[x][1]=(f[x][1]*f[E[i]][0]+f[x][0]*f[E[i]][1])%mod;
f[x][0]=f[x][0]*f[E[i]][0]%mod;
}
}
else
{
for(int i=Ls[x]; i; i=next[i]) if(E[i]!=p) solve(E[i],x);
d[0]=0;
for(int i=Ls[x]; i; i=next[i]) if(E[i]!=p) d[++d[0]]=E[i];
if(d[0]==1)
{
f[x][0]=f[d[1]][1],f[x][1]=-f[d[1]][0];
return;
}
ll sum=1; for(int i=1; i<=d[0]; i++) sum=sum*f[d[i]][0]%mod;
g[0][0]=1,g[1][0]=f[d[1]][1],g[1][1]=-f[d[1]][0];
for(int i=2; i<=d[0]; i++)
{
g[i][0]=(f[d[i]][1]*g[i-1][0]-f[d[i]][0]*f[d[i-1]][0]%mod*g[i-2][0])%mod;
g[i][1]=(f[d[i]][1]*g[i-1][1]-f[d[i]][0]*f[d[i-1]][0]%mod*g[i-2][1])%mod;
}
f[x][1]=(sum*2*((d[0]&1)?-1:1)+g[d[0]][1]-g[d[0]-1][0]*f[d[d[0]]][0])%mod;
f[x][0]=g[d[0]][0];
}
}
int main()
{
freopen("cactus.in","r",stdin); freopen("cactus.out","w",stdout);
scanf("%d%d",&n,&m);
cnt=1,nn=n;
for(int i=1,x,y; i<=m; i++) scanf("%d%d",&x,&y),add(x,y);
tarjan(1);
solve(1,0);
printf("%lld",(f[1][1]+mod)%mod);
}