刚了这么久就是没有往burnside靠。
题目大意:
求有多少个不同的n*m的01矩阵。
两个矩阵相同,定义为:通过每次交换两行或两列若干次之后完全相同。
n*m<=550,答案模1e9+7
题解:
b
u
r
n
s
i
d
e
burnside
burnside引理告诉我们:
不
同
数
=
∑
每
种
置
换
不
动
点
个
数
置
换
数
不同数={\sum_{每种置换}不动点个数\over 置换数}
不同数=置换数∑每种置换不动点个数
显然对于一维的置换就是一个排列,二维的置换就是两个排列结合起来。
因此置换数为 n ! ∗ m ! n!*m! n!∗m!.
算这种东西肯定用
p
o
l
y
a
polya
polya定理方便:
如果只是一维的情况,那么
A
n
s
=
2
环
的
个
数
Ans=2^{环的个数}
Ans=2环的个数
考虑二维,对于一个长度为x的环和一个长度为y的环,显然形成的二维环的长度是
l
c
m
(
x
,
y
)
lcm(x,y)
lcm(x,y),那么环的个数就是
x
y
/
l
c
m
(
x
,
y
)
=
g
c
d
(
x
,
y
)
xy/lcm(x,y)=gcd(x,y)
xy/lcm(x,y)=gcd(x,y)
那么对于确定的置换, A n s = ∏ 2 g c d ( a [ i ] , b [ j ] ) Ans=\prod2^{gcd(a[i],b[j])} Ans=∏2gcd(a[i],b[j]),a和b为各维的各个环的长度。
暴力肯定是不行的,考虑优化这个东西。
n ∗ m < = 550 n*m<=550 n∗m<=550,敏锐地意识到 m i n ( n , m ) < = 5 50 = 23 min(n,m)<=\sqrt 550=23 min(n,m)<=550=23
但是显然 23 ! 23! 23!的也挂了,我们当然不用枚举具体的置换,我们只需要枚举一个整数划分,代表了各个环的长度,然后去算有多少个对应的排列。
假设这个整数划分是
a
a
a,首先是广义组合数来算,即
n
!
∏
a
[
i
]
!
{n!\over \prod a[i]!}
∏a[i]!n!,,由于长度为
k
k
k的环的排列数为
(
k
−
1
)
!
(k-1)!
(k−1)!,再乘上去,变成了
n
∏
a
[
i
]
n\over\prod a[i]
∏a[i]n,其实长度相同的环的会重复算,设
b
[
j
]
b[j]
b[j]表示长度为i的环的个数,还要除以b[j]!,最后结果为:
s
u
m
=
n
∏
a
[
i
]
∏
b
[
j
]
sum={n\over \prod a[i]\prod b[j]}
sum=∏a[i]∏b[j]n
那么使 n < = m n<=m n<=m,对 n n n进行整数拆分,对于一种整数拆分,可以处理一个 c n t [ x ] = ∏ i = 1 ∣ a ∣ 2 g c d ( x , a [ i ] ) cnt[x]=\prod_{i=1}^{|a|}2^{gcd(x,a[i])} cnt[x]=∏i=1∣a∣2gcd(x,a[i]),然后对 m m m进行整数拆分的dp,若分成一个 x x x,则乘上一个 c n t [ x ] cnt[x] cnt[x],还有就是之前的计算对应排列的方案数,这个是一样的。
复杂度为:
n
<
=
m
,
O
(
B
(
n
)
∗
m
∗
m
∗
l
o
g
m
)
n<=m,O(B(n)*m*m*log~m)
n<=m,O(B(n)∗m∗m∗log m),其中的log是由
∑
i
=
1
m
m
/
i
≈
m
l
o
g
m
\sum_{i=1}^mm/i≈mlogm
∑i=1mm/i≈mlogm来的。
B ( n ) B(n) B(n)表示n的整数拆分数。
当n=23时,整数拆分为1000多种
如果n大了,虽然B(n)大了,m就变小了,因此是跑得过的。
感谢Samjia的指导。
Code:
#include<cstdio>
#include<algorithm>
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
using namespace std;
const int mo = 1e9 + 7;
const int N = 555;
int n, m;
ll ksm(ll x, ll y) {
ll s = 1;
for(; y; y /= 2, x = x * x % mo)
if(y & 1) s = s * x % mo;
return s;
}
ll ni[N], fac[N], a2[N], nf[N];
int a[N];
ll cnt[N];
int gcd(int x, int y) {
return !y ? x : gcd(y, x % y);
}
ll ans, f[N];
void dg(int x, int y) {
if(x == 0) {
ll s = fac[n];
fo(i, 1, a[0]) s = s * ni[a[i]] % mo;
int la = 1;
fo(i, 2, a[0] + 1) if(i > a[0] || a[i] != a[i - 1]) {
s = s * nf[i - la] % mo;
la = i;
}
fo(i, 1, m) {
cnt[i] = 1;
fo(j, 1, a[0]) cnt[i] *= a2[gcd(i, a[j])], cnt[i] %= mo;
}
fo(i, 0, m) f[i] = 0;
f[0] = fac[m];
fo(i, 1, m) {
fd(j, m, i) {
ll mi = 1;
for(int k = 1; i * k <= j; k ++) {
mi = mi * ni[i] % mo * cnt[i] % mo;
f[j] = (f[j] + f[j - i * k] * mi % mo * nf[k]) % mo;
}
}
}
ans = (ans + f[m] * s) % mo;
return;
}
fo(i, y, x) {
a[++ a[0]] = i;
dg(x - i, i);
a[0] --;
}
}
int main() {
scanf("%d %d", &n, &m);
if(n > m) swap(n, m);
fac[0] = 1; fo(i, 1, m) fac[i] = fac[i - 1] * i % mo;
nf[m] = ksm(fac[m], mo - 2);
fd(i, m, 1) nf[i - 1] = nf[i] * i % mo;
fo(i, 1, m) ni[i] = ksm(i, mo - 2);
a2[0] = 1; fo(i, 1, m) a2[i] = a2[i - 1] * 2 % mo;
dg(n, 1);
printf("%lld", ans * nf[n] % mo * nf[m] % mo);
}