把某一种点置换
g
g
分解成若干个轮换的乘积,注意到如果轮换大小构成的多重集相同,对答案产生的贡献也相同。
因此我们只需要枚举轮换大小构成的多重集,设
H
H
表示给定轮换大小的多重集时不动点的个数,则:
L
=
1
n
!
∑
l
1
≤
l
2
≤
.
.
.
≤
l
k
l
1
+
l
2
+
.
.
.
+
l
k
=
n
n
!
∏
n
i
=
1
(
l
i
−
1
)
!
∏
k
i
=
1
l
i
!
∏
n
i
=
1
c
i
!
H
(
l
1
,
l
2
,
.
.
.
,
l
k
)
=
∑
l
1
≤
l
2
≤
.
.
.
≤
l
k
l
1
+
l
2
+
.
.
.
+
l
k
=
n
1
∏
k
i
=
1
l
i
∏
n
i
=
1
c
i
!
H
(
l
1
,
l
2
,
.
.
.
,
l
k
)
L
=
n
!
1
l
1
+
l
2
+
.
.
.
+
l
k
=
n
l
1
≤
l
2
≤
.
.
.
≤
l
k
∑
i
=
1
∏
k
l
i
!
i
=
1
∏
n
c
i
!
n
!
i
=
1
∏
n
(
l
i
−
1
)
!
H
(
l
1
,
l
2
,
.
.
.
,
l
k
)
=
l
1
+
l
2
+
.
.
.
+
l
k
=
n
l
1
≤
l
2
≤
.
.
.
≤
l
k
∑
i
=
1
∏
k
l
i
i
=
1
∏
n
c
i
!
1
H
(
l
1
,
l
2
,
.
.
.
,
l
k
)
第一行
H
H
前面的系数表示轮换大小分别为
l
1
,
l
2
,
.
.
.
,
l
k
l
1
,
l
2
,
.
.
.
,
l
k
的排列个数,其中
c
i
c
i
表示
l
1
,
l
2
,
.
.
.
,
l
k
l
1
,
l
2
,
.
.
.
,
l
k
中
i
i
的出现次数。
具体地,排列个数的计算可以这样考虑:
枚举所有排列 (
n
!
n
!
),先不考虑每个轮换内部的构成,即除以
∏
k
i
=
1
l
i
!
i
=
1
∏
k
l
i
!
;
每个轮换可以看做一个有向环,
l
i
l
i
个点构成有向环的方案数是
(
l
i
−
1
)
!
(
l
i
−
1
)
!
,所以要乘以
∏
k
i
=
1
(
l
i
−
1
)
!
i
=
1
∏
k
(
l
i
−
1
)
!
;
大小相同的轮换之间是无序的,所以要除以
∏
n
i
=
1
c
i
!
i
=
1
∏
n
c
i
!
。
考虑怎样计算
H
(
l
1
,
l
2
,
.
.
.
,
l
k
)
H
(
l
1
,
l
2
,
.
.
.
,
l
k
)
。
对于第
i
i
个轮换,轮换内距离相同的所有点对的连边情况相同,而距离共有
⌊
l
i
2
⌋
⌊
2
l
i
⌋
种,总的连边情况共有
2
⌊
l
i
2
⌋
2
⌊
2
l
i
⌋
种。
若
l
i
l
i
为奇数,固定距离
x
x
时,每个点都恰好可以引出两条边,因此对于每一种连边情况都能保证轮换内每个点度数的奇偶性不变。
若
l
i
l
i
为偶数,
当
x
<
l
i
2
x
<
2
l
i
时,情况和
l
i
l
i
为奇数相同。
当
x
=
l
i
2
x
=
2
l
i
时,每个点只能恰好引出一条边,因此每一种连边情况都恰好能使轮换内每个点度数的奇偶性改变。
考虑任意两个轮换
i
,
j
i
,
j
之间的连边情况,枚举两个轮换上的点对
(
x
,
y
)
(
x
,
y
)
,则将
(
x
,
y
)
(
x
,
y
)
同时顺次移动得到的所有点对和
(
x
,
y
)
(
x
,
y
)
的连边情况相同。
因此共有
l
i
l
j
l
c
m
(
l
i
,
l
j
)
=
gcd
(
l
i
,
l
j
)
l
c
m
(
l
i
,
l
j
)
l
i
l
j
=
g
cd
(
l
i
,
l
j
)
组点对,每组点对的连边情况相同,且对于第
i
i
个轮换上的每一点都恰好可以引出
e
1
=
l
j
gcd
(
l
i
,
l
j
)
e
1
=
g
cd
(
l
i
,
l
j
)
l
j
条边,对于第
j
j
个轮换上的每一点都恰好可以引出
e
2
=
l
i
gcd
(
l
i
,
l
j
)
e
2
=
g
cd
(
l
i
,
l
j
)
l
i
条边。
若
e
1
e
1
和
e
2
e
2
都为偶数,任意一种连边情况都不会改变轮换
i
,
j
i
,
j
上每个点度数的奇偶性;
若
e
1
e
1
和
e
2
e
2
都为奇数,任意一种连边情况都恰好会使轮换
i
,
j
i
,
j
上每个点度数的奇偶性改变;
若
e
1
e
1
、
e
2
e
2
中恰好有一个为奇数,任意一种连边情况都恰好会使某一个轮换上每个点度数的奇偶性改变。
给定
k
k
个点和若干条边,每个点的初始点权为
0
0
,每个点
x
x
可以有
c
n
t
p
x
c
n
t
p
x
次机会使点权异或
1
1
,每条边
(
x
,
y
)
(
x
,
y
)
可以有
c
n
t
e
(
x
,
y
)
c
n
t
e
(
x
,
y
)
次机会使
x
,
y
x
,
y
两个端点的点权同时异或
1
1
,询问使得每个点最终的点权仍然都为
0
0
的方案数。
从
DFS树
的叶子结点开始往上推出每条树边的状态,对一个点
x
x
指向其父结点的边操作当且仅当
x
x
的点权为
1
1
,并且每次只能使点权为
1
1
的点的个数减少
2
2
或不变,因此树边的方案唯一确定且合法。
于是一个点数为
s
s
的连通块的方案为
2
max
{
(
∑
c
n
t
p
x
)
−
1
,
0
}
+
(
∑
c
n
t
e
(
x
,
y
)
)
−
s
+
1
2
max
{
(
∑
c
n
t
p
x
)
−
1
,
0
}
+
(
∑
c
n
t
e
(
x
,
y
)
)
−
s
+
1
。
设
p
n
p
n
表示
n
n
的拆分数,时间复杂度
O
(
n
2
p
n
)
O
(
n
2
p
n
)
,可以通过。
Code
#include<bits/stdc++.h>constint N =55;constint M =125e3+5;constint mod =998244353;int n, m, ans;int fra[N], inv_fra[N], inv[N];int ex[M], g[N][N], l[N], c[N];int sze[N], fa[N], cnt_p[N], cnt_e[N];template<classT>inline T Max(T x, T y){return x > y ? x : y;}inlinevoidadd(int&x,int y){
x += y;
x >= mod ? x -= mod :0;}inlinevoidmul(int&x,int y){
x =1ll* x * y % mod;}inlineintquick_pow(int x,int k){int res =1;while(k){if(k &1)
res =1ll* res * x % mod;
x =1ll* x * x % mod;
k >>=1;}return res;}inlineintufs_find(int x){return fa[x]== x ? x : fa[x]=ufs_find(fa[x]);}inlinevoidufs_merge(int x,int y,int e){int tx =ufs_find(x),
ty =ufs_find(y);if(tx != ty){if(sze[tx]> sze[ty])
std::swap(tx, ty);
fa[tx]= ty;
sze[ty]+= sze[tx];
cnt_p[ty]+= cnt_p[tx];
cnt_e[ty]+= cnt_e[tx]+ e;}else
cnt_e[ty]+= e;}inlinevoidsolve(int k){int res =1;for(int i =1; i <= k;++i)mul(res, inv[l[i]]);for(int i =1; i <= n;++i)mul(res, inv_fra[c[i]]);for(int i =1; i <= k;++i){
fa[i]= i;
cnt_e[i]=0;
cnt_p[i]=0;
sze[i]=1;}for(int i =1; i <= k;++i){if(!(l[i]&1))++cnt_p[i];mul(res, ex[l[i]-1>>1]);}for(int i =1; i < k;++i)for(int j = i +1; j <= k;++j){int e = g[l[i]][l[j]];bool e1 =(l[j]/ e)&1,
e2 =(l[i]/ e)&1;if(e1 && e2)ufs_merge(i, j, e);elseif(e1)
cnt_p[ufs_find(i)]+= e;elseif(e2)
cnt_p[ufs_find(j)]+= e;elsemul(res, ex[e]);}for(int i =1; i <= k;++i)if(ufs_find(i)== i)mul(res, ex[Max(0, cnt_p[i]-1)+ cnt_e[i]- sze[i]+1]);add(ans, res);}inlinevoiddfs(int k,int s,int t){if(!s)returnsolve(k -1);for(int i = t; i <= s;++i){
l[k]= i;++c[i];dfs(k +1, s - i, i);--c[i];}}intmain(){scanf("%d",&n);
fra[0]=1;for(int i =1; i <= n;++i)
fra[i]=1ll* fra[i -1]* i % mod;
inv_fra[n]=quick_pow(fra[n], mod -2);for(int i = n; i >=1;--i)
inv_fra[i -1]=1ll* inv_fra[i]* i % mod;
inv[0]= inv[1]=1;for(int i =2; i <= n;++i)
inv[i]=1ll*(mod - mod / i)* inv[mod % i]% mod;
ex[0]=1;for(int i =1, im = n * n * n; i <= im;++i)add(ex[i]= ex[i -1], ex[i -1]);for(int i =1; i <= n;++i)for(int j =1; j <= n;++j)
g[i][j]= std::__gcd(i, j);dfs(1, n,1);printf("%d\n", ans);}