CF2040F Number of Cubes
回顾 burnside 引理。对于一个群
即
对于本题,
那么对于
-
\forall i \in [0,a),j\in[0,b),l\in[0,c)$,$A_{i,j,l}=A_{(i+x)\bmod a,(j+y)\bmod b,(l+z)\bmod c} -
思考这个问题并不容易,可以先思考简单的情况:
给定正整数
手填一下会发现满足条件的序列的循环节长为
一样使用
在此基础上,每个循环节可以分到
回到到三维的情况,不难想象对于动作
这意味着循环节只和动作本身有关,而在这个立方体的哪一个点上施加这个动作得到的循环节数量都是相同的。
至此,我们有了一个单组数据
因为
故最终的做法是用三层循环枚举
时间复杂度
void solve() {
int a, b, c, k;
cin >> a >> b >> c >> k;
vector<int> d(k);
int A = 0;
for (int i = 0; i < k; i++) {
cin >> d[i];
A = gcd(A, d[i]);
}
vector<mint> f(A + 1);
auto calc = [&] (int T) {
f[T] = 1;
mint all = a * b * c / T;
for (auto x : d) {
f[T] *= C(all, x / T);
all -= x / T;
}
};
for (int i = 1; i * i <= A; i++) {
if (A % i == 0) {
calc(i);
calc(A / i);
}
}
auto getd = [&] (int x, vector<int> &vec) {
for (int i = 1; i * i <= x; i++) {
if (x % i == 0) {
vec.push_back(i);
if (i * i < x) {
vec.push_back(x / i);
}
}
}
};
vector<int> da, db, dc;
getd(a, da);
getd(b, db);
getd(c, dc);
mint ans = 0;
for (auto x : da) {
for (auto y : db) {
for (auto z : dc) {
int T = lcm(x, lcm(y, z));
if (A % T == 0) {
ans += f[T] * phi[x] * phi[y] * phi[z];
}
}
}
}
ans /= a * b * c;
cout << ans << "\n";
}