来一篇真的好怪的题解。
正文
首先有一个很低级的做法,就是每次询问直接拉出来 \(\frac np\) 个数,然后排好序,用另外 \(\frac nq\) 个数在那上面二分就结束了。
这样做的复杂度是 \(\mathcal{O}(nm\log n)\)。
然后我们记忆化一下,发现小的 \(q,p\) 出现次数不会很多,就很好,至于复杂度我就不会算了。
不知道有多少分。
然后虽然不知道复杂度是多少但是总之考虑砍掉 \(\log\)。
考虑阈值分治,定义阈值 \(B\)。
给小于 \(B\) 的 \(q,p\) 提前处理好排过序的数组,这样复杂度就优很多,定义阈值为 \(\log n\) 就可以砍掉 \(\log\),不过显然阈值拉高一点是好的。
考虑继续优化复杂度。
不过我们可以先尝试交一发。
结果就 AC 了。
所以就不优化复杂度了,反正我也不会算,就当它是对的算了。
代码
// code by 樓影沫瞬_Hz17
#include <bits/extc++.h>using namespace std;
constexpr int N = 5e4 + 10, B = 100; // 随便乱设的阈值uint n, m;
uint16_t a[N];uint16_t *wer[B + 10][B + 10], *ed[B + 10][B + 10];
uint16_t wsz[B + 10][B + 10]; // 因为一些原因我对 vector 有阴影了,所以不用,我选择手写
using puu = pair<uint, uint>;
puu b[N];using __gnu_pbds::gp_hash_table; // 好长的记忆化数组…………
gp_hash_table<uint16_t, gp_hash_table<uint16_t, gp_hash_table<uint16_t, unordered_map<uint16_t, uint> > > > ans;
uint16_t tmp1[N], tmp2[N]; // 相当于提前申请点内存signed main() {#ifndef ONLINE_JUDGEfreopen("in.ru", "r", stdin);freopen("out.ru", "w", stdout);#endifios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin >> n >> m;for(uint i = 1; i <= n; i ++) cin >> a[i], b[i] = {a[i], i};sort(b + 1, b + 1 + n, [](const puu &a, const puu &b) { return a.first < b.first; }); // 现在排了,一会省点排序for(uint p = 1; p <= B; p ++) for(uint i = 1; i <= n; i ++) wsz[p][i % p] ++; for(uint p = 1; p <= B; p ++) for(uint les = 0; les < p; les ++) wer[p][les] = ed[p][les] = new uint16_t[wsz[p][les] + 10];for(uint p = 1; p <= B; p ++) for(uint i = 1; i <= n; i ++) *ed[p][b[i].second % p] ++ = b[i].first; // 省在这了auto PreCalc = [](uint16_t *st1, uint16_t *ed1, uint16_t *st2, uint16_t *ed2) -> puu {uint res1 = 0, res2 = 0, ct1 = 0, ct2 = 0;// 如果两个数组都有序就直接归并,别二分了while(st1 != ed1 and st2 != ed2) {while(*st1 < *st2 and st1 != ed1) res1 += ct2, st1 ++, ct1 ++;while(*st1 == *st2 and st1 != ed1) res1 += ct2, st1 ++, ct1 ++, res2 --;while(*st2 < *st1 and st2 != ed2) res2 += ct1, st2 ++, ct2 ++;}while(st2 != ed2) res2 += ct1, st2 ++;while(st1 != ed1) res1 += ct2, st1 ++;return {res1, res2};}; // 本来打算预处理所以函数名叫预处理uint16_t p, x, q, y;while(m --) {cin >> p >> x >> q >> y;if(ans[p][q][x].count(y)) cout << ans[p][q][x][y] << '\n';else if(p <= B and q <= B) { // 分类讨论好长啊…………auto [res1, res2] = PreCalc(wer[p][x], ed[p][x], wer[q][y], ed[q][y]);ans[p][q][x][y] = res2;ans[q][p][y][x] = res1;cout << ans[p][q][x][y] << '\n';}else { uint ans = 0; if(p <= B) { // 好长啊…………uint16_t *st1 = wer[p][x], *ed1 = ed[p][x];for(uint i = y == 0 ? q : y; i <= n; i += q) ans += lower_bound(st1, ed1, a[i]) - st1;}else if(q <= B) { // 好长啊…………uint16_t *st2 = wer[q][y], *ed2 = ed[q][y];for(uint i = x == 0 ? p : x; i <= n; i += p) ans += ed2 - upper_bound(st2, ed2, a[i]);}else { // 真的好长啊…………uint16_t *st1 = tmp1, *ed1 = tmp1;for(uint i = x == 0 ? p : x; i <= n; i += p) *ed1 ++ = a[i];sort(st1, ed1);for(uint i = y == 0 ? q : y; i <= n; i += q) ans += lower_bound(st1, ed1, a[i]) - st1;}::ans[p][q][x][y] = ans;cout << ans << '\n';}}
}