模擬賽碰到的題,來寫一發。
關於迴圈節問題肯定是要找最小迴圈節,而二維矩形明顯能拆成兩個一維問題,最後乘起來即可,下面以行為例。
然後考慮迴圈節一定是 \(n\) 的因子,於是對於一個質因數 \(p\),每次考慮檢驗前 \(\frac{n}{p^x}\) 箇中,按照 \(\frac{n}{p^{x+1}}\) 分成的 \(p\) 塊是否相等,不相等時即可知道最小迴圈節有多少個 \(p\)。
檢驗的話考慮倍增,每次詢問 \([1,2^i]\) 塊和 \([2^i+1,2^{i+1}]\) 塊是否相等,最後一點單獨處理。
總次數為 \(\sum k_i \times \log p_i= \sum \log p_i^{k_i}= \log n\)。
int simons(int a, int b, int c, int d, int e, int f) {
cout << "? " << a << ' ' << b << ' ' << c << ' ' << d << ' ' << e << ' ' << f << endl;
int w;
cin >> w;
return w;
}
int truth(int n, int m) {
auto div = [&](int x) {
vector < pair <int, int> > p;
for (int i = 2; i <= x; i++) {
if (x % i == 0) {
int c = 0;
while (x % i == 0) x /= i, c++;
p.emplace_back(i, c);
}
}
return p;
};
auto p = div(n);
int nres = 1;
for (auto [x, c] : p) {
int lst = n, cc = 0;
for (int i = 1; i <= c; i++) {
int nw = lst / x;
auto chk = [&](int r) -> bool {
int lst = 0;
for (int i = 1; i * 2 <= x; i *= 2) {
if (!simons(i * r, m, 1, 1, i * r + 1, 1)) return 0;
lst = i * 2;
}
lst = x - lst;
if (!lst) return 1;
return simons(lst * r, m, 1, 1, r * x - lst * r + 1, 1);
};
if (!chk(nw)) break;
lst /= x;
cc++;
}
nres *= pow(x, c - cc);
}
p = div(m);
int mres = 1;
for (auto [x, c] : p) {
int lst = m, cc = 0;
for (int i = 1; i <= c; i++) {
int nw = lst / x;
auto chk = [&](int r) -> bool {
int lst = 0;
for (int i = 1; i * 2 <= x; i *= 2) {
if (!simons(n, i * r, 1, 1, 1, i * r + 1)) return 0;
lst = i * 2;
}
lst = x - lst;
if (!lst) return 1;
return simons(n, lst * r, 1, 1, 1, r * x - lst * r + 1);
};
if (!chk(nw)) break;
lst /= x;
cc++;
}
mres *= pow(x, c - cc);
}
nres = n / nres, mres = m / mres;
// debug(nres, mres);
auto cal = [&](int x) {
int cc = 0;
for (int i = 1; i <= x; i++) {
if (x % i == 0) cc++;
}
return cc;
};
return cal(nres) * cal(mres);
}