Y
K
N
U
F

真的好怪题解:P14314 [Aboi Round 2] Oneshot

来一篇真的好怪的题解。

正文

首先有一个很低级的做法,就是每次询问直接拉出来 \(\frac np\) 个数,然后排好序,用另外 \(\frac nq\) 个数在那上面二分就结束了。

这样做的复杂度是 \(\mathcal{O}(nm\log n)\)

然后我们记忆化一下,发现小的 \(q,p\) 出现次数不会很多,就很好,至于复杂度我就不会算了。

不知道有多少分。

然后虽然不知道复杂度是多少但是总之考虑砍掉 \(\log\)

考虑阈值分治,定义阈值 \(B\)

给小于 \(B\)\(q,p\) 提前处理好排过序的数组,这样复杂度就优很多,定义阈值为 \(\log n\) 就可以砍掉 \(\log\),不过显然阈值拉高一点是好的。

同学说不知道 \(\log\) 怎么没的,我们这样考虑,进行 \(\frac nq\) 次二分,时间是 \(\frac nq \log \frac np\) 的,而如果 \(q\) 比较大那么 \(\log\) 就没了,然后排序复杂度是 \(\frac np \log \frac np\) 所以 \(p\) 比较大 \(\log\) 就也没了,所以只要处理小的 \(q,p\) 就可以砍掉 \(\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_JUDGE
		freopen("in.ru", "r", stdin);
		freopen("out.ru", "w", stdout);
	#endif
	ios::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';
		}
	}
}
posted @ 2025-11-22 19:55  樓影沫瞬_Hz17  阅读(12)  评论(1)    收藏  举报