原题链接.
刚了这么久就是没有往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
#include
#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);
}